diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java index 9c4d2b1cd9cbfe6105ad1bfac3783dbbca085192..6b06cc873e0e3bd36375a0575d1ecb58698783ec 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java @@ -1,9 +1,14 @@ package org.postgresql.jdbc1; +import java.io.*; + +import java.math.BigDecimal; import java.sql.*; -import org.postgresql.util.PSQLException; +import java.util.Vector; +import org.postgresql.largeobject.*; +import org.postgresql.util.*; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.2 2002/07/24 22:08:39 barry Exp $ * This class defines methods of the jdbc1 specification. This class is * extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2 * methods. The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement @@ -34,6 +39,56 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme private static final short BACKSLASH = 2; private static final short ESC_TIMEDATE = 3; + // Some performance caches + private StringBuffer sbuf = new StringBuffer(); + + //Used by the preparedstatement style methods + protected String sql; + protected String[] templateStrings; + protected String[] inStrings; + + + + public AbstractJdbc1Statement (AbstractJdbc1Connection connection) + { + this.connection = connection; + } + + public AbstractJdbc1Statement (AbstractJdbc1Connection connection, String sql) throws SQLException + { + this.sql = sql; + this.connection = connection; + parseSqlStmt(); // this allows Callable stmt to override + } + + protected void parseSqlStmt () throws SQLException { + Vector v = new Vector(); + boolean inQuotes = false; + int lastParmEnd = 0, i; + + for (i = 0; i < sql.length(); ++i) + { + int c = sql.charAt(i); + + if (c == '\'') + inQuotes = !inQuotes; + if (c == '?' && !inQuotes) + { + v.addElement(sql.substring (lastParmEnd, i)); + lastParmEnd = i + 1; + } + } + v.addElement(sql.substring (lastParmEnd, sql.length())); + + templateStrings = new String[v.size()]; + inStrings = new String[v.size() - 1]; + clearParameters(); + + for (i = 0 ; i < templateStrings.length; ++i) + templateStrings[i] = (String)v.elementAt(i); + } + + /* * Execute a SQL statement that retruns a single ResultSet * @@ -51,6 +106,18 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme return result; } + /* + * A Prepared SQL query is executed and its ResultSet is returned + * + * @return a ResultSet that contains the data produced by the + * * query - never null + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSet executeQuery() throws SQLException + { + return executeQuery(compileQuery()); + } + /* * Execute a SQL INSERT, UPDATE or DELETE statement. In addition * SQL statements that return nothing such as SQL DDL statements @@ -68,6 +135,20 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme return this.getUpdateCount(); } + /* + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, + * SQL statements that return nothing such as SQL DDL statements can + * be executed. + * + * @return either the row count for INSERT, UPDATE or DELETE; or + * * 0 for SQL statements that return nothing. + * @exception SQLException if a database access error occurs + */ + public int executeUpdate() throws SQLException + { + return executeUpdate(compileQuery()); + } + /* * Execute a SQL statement that may return multiple results. We * don't have to worry about this since we do not support multiple @@ -101,6 +182,20 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet()); } + /* + * Some prepared statements return multiple results; the execute method + * handles these complex statements as well as the simpler form of + * statements handled by executeQuery and executeUpdate + * + * @return true if the next result is a ResultSet; false if it is an + * * update count or there are no more results + * @exception SQLException if a database access error occurs + */ + public boolean execute() throws SQLException + { + return execute(compileQuery()); + } + /* * setCursorName defines the SQL cursor name that will be used by * subsequent execute methods. This name can then be used in SQL @@ -466,6 +561,743 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme return ((AbstractJdbc1ResultSet)result).getLastOID(); } + /* + * Set a parameter to SQL NULL + * + * <p><B>Note:</B> You must specify the parameters SQL type (although + * PostgreSQL ignores it) + * + * @param parameterIndex the first parameter is 1, etc... + * @param sqlType the SQL type code defined in java.sql.Types + * @exception SQLException if a database access error occurs + */ + public void setNull(int parameterIndex, int sqlType) throws SQLException + { + set(parameterIndex, "null"); + } + + /* + * Set a parameter to a Java boolean value. The driver converts this + * to a SQL BIT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBoolean(int parameterIndex, boolean x) throws SQLException + { + set(parameterIndex, x ? "'t'" : "'f'"); + } + + /* + * Set a parameter to a Java byte value. The driver converts this to + * a SQL TINYINT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setByte(int parameterIndex, byte x) throws SQLException + { + set(parameterIndex, Integer.toString(x)); + } + + /* + * Set a parameter to a Java short value. The driver converts this + * to a SQL SMALLINT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setShort(int parameterIndex, short x) throws SQLException + { + set(parameterIndex, Integer.toString(x)); + } + + /* + * Set a parameter to a Java int value. The driver converts this to + * a SQL INTEGER value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setInt(int parameterIndex, int x) throws SQLException + { + set(parameterIndex, Integer.toString(x)); + } + + /* + * Set a parameter to a Java long value. The driver converts this to + * a SQL BIGINT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setLong(int parameterIndex, long x) throws SQLException + { + set(parameterIndex, Long.toString(x)); + } + + /* + * Set a parameter to a Java float value. The driver converts this + * to a SQL FLOAT value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setFloat(int parameterIndex, float x) throws SQLException + { + set(parameterIndex, Float.toString(x)); + } + + /* + * Set a parameter to a Java double value. The driver converts this + * to a SQL DOUBLE value when it sends it to the database + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setDouble(int parameterIndex, double x) throws SQLException + { + set(parameterIndex, Double.toString(x)); + } + + /* + * Set a parameter to a java.lang.BigDecimal value. The driver + * converts this to a SQL NUMERIC value when it sends it to the + * database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException + { + if (x == null) + setNull(parameterIndex, Types.OTHER); + else + { + set(parameterIndex, x.toString()); + } + } + + /* + * Set a parameter to a Java String value. The driver converts this + * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments + * size relative to the driver's limits on VARCHARs) when it sends it + * to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setString(int parameterIndex, String x) throws SQLException + { + // if the passed string is null, then set this column to null + if (x == null) + setNull(parameterIndex, Types.OTHER); + else + { + // use the shared buffer object. Should never clash but this makes + // us thread safe! + synchronized (sbuf) + { + sbuf.setLength(0); + int i; + + sbuf.append('\''); + for (i = 0 ; i < x.length() ; ++i) + { + char c = x.charAt(i); + if (c == '\\' || c == '\'') + sbuf.append((char)'\\'); + sbuf.append(c); + } + sbuf.append('\''); + set(parameterIndex, sbuf.toString()); + } + } + } + + /* + * Set a parameter to a Java array of bytes. The driver converts this + * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's + * size relative to the driver's limits on VARBINARYs) when it sends + * it to the database. + * + * <p>Implementation note: + * <br>With org.postgresql, this creates a large object, and stores the + * objects oid in this column. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBytes(int parameterIndex, byte x[]) throws SQLException + { + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports the bytea datatype for byte arrays + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + setString(parameterIndex, PGbytea.toPGString(x)); + } + } + else + { + //Version 7.1 and earlier support done as LargeObjects + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + lob.write(x); + lob.close(); + setInt(parameterIndex, oid); + } + } + + /* + * Set a parameter to a java.sql.Date value. The driver converts this + * to a SQL DATE value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setDate(int parameterIndex, java.sql.Date x) throws SQLException + { + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + set(parameterIndex, "'" + x.toString() + "'"); + } + } + + /* + * Set a parameter to a java.sql.Time value. The driver converts + * this to a SQL TIME value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1...)); + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setTime(int parameterIndex, Time x) throws SQLException + { + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + set(parameterIndex, "'" + x.toString() + "'"); + } + } + + /* + * Set a parameter to a java.sql.Timestamp value. The driver converts + * this to a SQL TIMESTAMP value when it sends it to the database. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException + { + if (null == x) + { + setNull(parameterIndex, Types.OTHER); + } + else + { + // Use the shared StringBuffer + synchronized (sbuf) + { + sbuf.setLength(0); + sbuf.append("'"); + //format the timestamp + //we do our own formating so that we can get a format + //that works with both timestamp with time zone and + //timestamp without time zone datatypes. + //The format is '2002-01-01 23:59:59.123456-0130' + //we need to include the local time and timezone offset + //so that timestamp without time zone works correctly + int l_year = x.getYear() + 1900; + sbuf.append(l_year); + sbuf.append('-'); + int l_month = x.getMonth() + 1; + if (l_month < 10) sbuf.append('0'); + sbuf.append(l_month); + sbuf.append('-'); + int l_day = x.getDate(); + if (l_day < 10) sbuf.append('0'); + sbuf.append(l_day); + sbuf.append(' '); + int l_hours = x.getHours(); + if (l_hours < 10) sbuf.append('0'); + sbuf.append(l_hours); + sbuf.append(':'); + int l_minutes = x.getMinutes(); + if (l_minutes < 10) sbuf.append('0'); + sbuf.append(l_minutes); + sbuf.append(':'); + int l_seconds = x.getSeconds(); + if (l_seconds < 10) sbuf.append('0'); + sbuf.append(l_seconds); + // Make decimal from nanos. + char[] l_decimal = {'0','0','0','0','0','0','0','0','0'}; + char[] l_nanos = Integer.toString(x.getNanos()).toCharArray(); + System.arraycopy(l_nanos, 0, l_decimal, l_decimal.length - l_nanos.length, l_nanos.length); + sbuf.append('.'); + if (connection.haveMinimumServerVersion("7.2")) { + sbuf.append(l_decimal,0,6); + } else { + // Because 7.1 include bug that "hh:mm:59.999" becomes "hh:mm:60.00". + sbuf.append(l_decimal,0,2); + } + //add timezone offset + int l_offset = -(x.getTimezoneOffset()); + int l_houros = l_offset/60; + if (l_houros >= 0) { + sbuf.append('+'); + } else { + sbuf.append('-'); + } + if (l_houros > -10 && l_houros < 10) sbuf.append('0'); + if (l_houros >= 0) { + sbuf.append(l_houros); + } else { + sbuf.append(-l_houros); + } + int l_minos = l_offset - (l_houros *60); + if (l_minos != 0) { + if (l_minos < 10) sbuf.append('0'); + sbuf.append(l_minos); + } + sbuf.append("'"); + set(parameterIndex, sbuf.toString()); + } + + } + } + + /* + * When a very large ASCII value is input to a LONGVARCHAR parameter, + * it may be more practical to send it via a java.io.InputStream. + * JDBC will read the data from the stream as needed, until it reaches + * end-of-file. The JDBC driver will do any necessary conversion from + * ASCII to the database char format. + * + * <P><B>Note:</B> This stream object can either be a standard Java + * stream object or your own subclass that implements the standard + * interface. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @param length the number of bytes in the stream + * @exception SQLException if a database access error occurs + */ + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException + { + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) + //As the spec/javadoc for this method indicate this is to be used for + //large String values (i.e. LONGVARCHAR) PG doesn't have a separate + //long varchar datatype, but with toast all text datatypes are capable of + //handling very large values. Thus the implementation ends up calling + //setString() since there is no current way to stream the value to the server + try + { + InputStreamReader l_inStream = new InputStreamReader(x, "ASCII"); + char[] l_chars = new char[length]; + int l_charsRead = l_inStream.read(l_chars, 0, length); + setString(parameterIndex, new String(l_chars, 0, l_charsRead)); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + catch (IOException l_ioe) + { + throw new PSQLException("postgresql.unusual", l_ioe); + } + } + else + { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data + setBinaryStream(parameterIndex, x, length); + } + } + + /* + * When a very large Unicode value is input to a LONGVARCHAR parameter, + * it may be more practical to send it via a java.io.InputStream. + * JDBC will read the data from the stream as needed, until it reaches + * end-of-file. The JDBC driver will do any necessary conversion from + * UNICODE to the database char format. + * + * <P><B>Note:</B> This stream object can either be a standard Java + * stream object or your own subclass that implements the standard + * interface. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException + { + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) + //As the spec/javadoc for this method indicate this is to be used for + //large String values (i.e. LONGVARCHAR) PG doesn't have a separate + //long varchar datatype, but with toast all text datatypes are capable of + //handling very large values. Thus the implementation ends up calling + //setString() since there is no current way to stream the value to the server + try + { + InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8"); + char[] l_chars = new char[length]; + int l_charsRead = l_inStream.read(l_chars, 0, length); + setString(parameterIndex, new String(l_chars, 0, l_charsRead)); + } + catch (UnsupportedEncodingException l_uee) + { + throw new PSQLException("postgresql.unusual", l_uee); + } + catch (IOException l_ioe) + { + throw new PSQLException("postgresql.unusual", l_ioe); + } + } + else + { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data + setBinaryStream(parameterIndex, x, length); + } + } + + /* + * When a very large binary value is input to a LONGVARBINARY parameter, + * it may be more practical to send it via a java.io.InputStream. + * JDBC will read the data from the stream as needed, until it reaches + * end-of-file. + * + * <P><B>Note:</B> This stream object can either be a standard Java + * stream object or your own subclass that implements the standard + * interface. + * + * @param parameterIndex the first parameter is 1... + * @param x the parameter value + * @exception SQLException if a database access error occurs + */ + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException + { + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports BinaryStream for for the PG bytea type + //As the spec/javadoc for this method indicate this is to be used for + //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate + //long binary datatype, but with toast the bytea datatype is capable of + //handling very large values. Thus the implementation ends up calling + //setBytes() since there is no current way to stream the value to the server + byte[] l_bytes = new byte[length]; + int l_bytesRead; + try + { + l_bytesRead = x.read(l_bytes, 0, length); + } + catch (IOException l_ioe) + { + throw new PSQLException("postgresql.unusual", l_ioe); + } + if (l_bytesRead == length) + { + setBytes(parameterIndex, l_bytes); + } + else + { + //the stream contained less data than they said + byte[] l_bytes2 = new byte[l_bytesRead]; + System.arraycopy(l_bytes, 0, l_bytes2, 0, l_bytesRead); + setBytes(parameterIndex, l_bytes2); + } + } + else + { + //Version 7.1 only supported streams for LargeObjects + //but the jdbc spec indicates that streams should be + //available for LONGVARBINARY instead + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + OutputStream los = lob.getOutputStream(); + try + { + // could be buffered, but then the OutputStream returned by LargeObject + // is buffered internally anyhow, so there would be no performance + // boost gained, if anything it would be worse! + int c = x.read(); + int p = 0; + while (c > -1 && p < length) + { + los.write(c); + c = x.read(); + p++; + } + los.close(); + } + catch (IOException se) + { + throw new PSQLException("postgresql.unusual", se); + } + // lob is closed by the stream so don't call lob.close() + setInt(parameterIndex, oid); + } + } + + + /* + * In general, parameter values remain in force for repeated used of a + * Statement. Setting a parameter value automatically clears its + * previous value. However, in coms cases, it is useful to immediately + * release the resources used by the current parameter values; this + * can be done by calling clearParameters + * + * @exception SQLException if a database access error occurs + */ + public void clearParameters() throws SQLException + { + int i; + + for (i = 0 ; i < inStrings.length ; i++) + inStrings[i] = null; + } + + /* + * Set the value of a parameter using an object; use the java.lang + * equivalent objects for integral values. + * + * <P>The given Java object will be converted to the targetSqlType before + * being sent to the database. + * + * <P>note that this method may be used to pass database-specific + * abstract data types. This is done by using a Driver-specific + * Java type and using a targetSqlType of java.sql.Types.OTHER + * + * @param parameterIndex the first parameter is 1... + * @param x the object containing the input parameter value + * @param targetSqlType The SQL type to be send to the database + * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC + * * types this is the number of digits after the decimal. For + * * all other types this value will be ignored. + * @exception SQLException if a database access error occurs + */ + public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException + { + if (x == null) + { + setNull(parameterIndex, Types.OTHER); + return; + } + switch (targetSqlType) + { + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + case Types.DECIMAL: + case Types.NUMERIC: + if (x instanceof Boolean) + set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0"); + else + set(parameterIndex, x.toString()); + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + setString(parameterIndex, x.toString()); + break; + case Types.DATE: + setDate(parameterIndex, (java.sql.Date)x); + break; + case Types.TIME: + setTime(parameterIndex, (Time)x); + break; + case Types.TIMESTAMP: + setTimestamp(parameterIndex, (Timestamp)x); + break; + case Types.BIT: + if (x instanceof Boolean) + { + set(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE"); + } + else + { + throw new PSQLException("postgresql.prep.type"); + } + break; + case Types.BINARY: + case Types.VARBINARY: + setObject(parameterIndex, x); + break; + case Types.OTHER: + setString(parameterIndex, ((PGobject)x).getValue()); + break; + default: + throw new PSQLException("postgresql.prep.type"); + } + } + + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException + { + setObject(parameterIndex, x, targetSqlType, 0); + } + + /* + * This stores an Object into a parameter. + * <p>New for 6.4, if the object is not recognised, but it is + * Serializable, then the object is serialised using the + * org.postgresql.util.Serialize class. + */ + public void setObject(int parameterIndex, Object x) throws SQLException + { + if (x == null) + { + setNull(parameterIndex, Types.OTHER); + return; + } + if (x instanceof String) + setString(parameterIndex, (String)x); + else if (x instanceof BigDecimal) + setBigDecimal(parameterIndex, (BigDecimal)x); + else if (x instanceof Short) + setShort(parameterIndex, ((Short)x).shortValue()); + else if (x instanceof Integer) + setInt(parameterIndex, ((Integer)x).intValue()); + else if (x instanceof Long) + setLong(parameterIndex, ((Long)x).longValue()); + else if (x instanceof Float) + setFloat(parameterIndex, ((Float)x).floatValue()); + else if (x instanceof Double) + setDouble(parameterIndex, ((Double)x).doubleValue()); + else if (x instanceof byte[]) + setBytes(parameterIndex, (byte[])x); + else if (x instanceof java.sql.Date) + setDate(parameterIndex, (java.sql.Date)x); + else if (x instanceof Time) + setTime(parameterIndex, (Time)x); + else if (x instanceof Timestamp) + setTimestamp(parameterIndex, (Timestamp)x); + else if (x instanceof Boolean) + setBoolean(parameterIndex, ((Boolean)x).booleanValue()); + else if (x instanceof PGobject) + setString(parameterIndex, ((PGobject)x).getValue()); + else + // Try to store java object in database + setSerialize(parameterIndex, connection.storeObject(x), x.getClass().getName() ); + } + + /* + * Returns the SQL statement with the current template values + * substituted. + * NB: This is identical to compileQuery() except instead of throwing + * SQLException if a parameter is null, it places ? instead. + */ + public String toString() + { + synchronized (sbuf) + { + sbuf.setLength(0); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + sbuf.append( '?' ); + else + sbuf.append (templateStrings[i]); + sbuf.append (inStrings[i]); + } + sbuf.append(templateStrings[inStrings.length]); + return sbuf.toString(); + } + } + + /* + * There are a lot of setXXX classes which all basically do + * the same thing. We need a method which actually does the + * set for us. + * + * @param paramIndex the index into the inString + * @param s a string to be stored + * @exception SQLException if something goes wrong + */ + protected void set(int paramIndex, String s) throws SQLException + { + if (paramIndex < 1 || paramIndex > inStrings.length) + throw new PSQLException("postgresql.prep.range"); + inStrings[paramIndex - 1] = s; + } + + /* + * Helper - this compiles the SQL query from the various parameters + * This is identical to toString() except it throws an exception if a + * parameter is unused. + */ + protected synchronized String compileQuery() + throws SQLException + { + sbuf.setLength(0); + int i; + + for (i = 0 ; i < inStrings.length ; ++i) + { + if (inStrings[i] == null) + throw new PSQLException("postgresql.prep.param", new Integer(i + 1)); + sbuf.append (templateStrings[i]).append (inStrings[i]); + } + sbuf.append(templateStrings[inStrings.length]); + return sbuf.toString(); + } + + /* + * Set a parameter to a tablerow-type oid reference. + * + * @param parameterIndex the first parameter is 1... + * @param x the oid of the object from org.postgresql.util.Serialize.store + * @param classname the classname of the java object x + * @exception SQLException if a database access error occurs + */ + private void setSerialize(int parameterIndex, long x, String classname) throws SQLException + { + // converts . to _, toLowerCase, and ensures length<32 + String tablename = Serialize.toPostgreSQL( classname ); + DriverManager.println("setSerialize: setting " + x + "::" + tablename ); + + // OID reference to tablerow-type must be cast like: <oid>::<tablename> + // Note that postgres support for tablerow data types is incomplete/broken. + // This cannot be just a plain OID because then there would be ambiguity + // between when you want the oid itself and when you want the object + // an oid references. + set(parameterIndex, Long.toString(x) + "::" + tablename ); + } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java index 10e8c5f4171db2f1f73b8494d0b8f344abe95dfa..dab157f97b624a6dcc3fa59ad329b17b8b3ece79 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java @@ -39,7 +39,7 @@ import java.math.*; * @see ResultSet */ -public class CallableStatement extends PreparedStatement implements java.sql.CallableStatement +public class CallableStatement extends Jdbc1PreparedStatement implements java.sql.CallableStatement { /* * @exception SQLException on failure diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java index 55e527bd4908c6c6d033978c3e9f64efffcf6078..249a41049b6ed2c3f86e043da65fbbf18cb9fa5c 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java @@ -6,7 +6,7 @@ import java.sql.*; import org.postgresql.Field; import org.postgresql.util.PSQLException; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1Connection.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1Connection.java,v 1.2 2002/07/24 22:08:40 barry Exp $ * This class implements the java.sql.Connection interface for JDBC1. * However most of the implementation is really done in * org.postgresql.jdbc1.AbstractJdbc1Connection @@ -21,7 +21,7 @@ public class Jdbc1Connection extends org.postgresql.jdbc1.AbstractJdbc1Connectio public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException { - return new org.postgresql.jdbc1.PreparedStatement(this, sql); + return new org.postgresql.jdbc1.Jdbc1PreparedStatement(this, sql); } //BJL TODO - merge callable statement logic from jdbc2 to jdbc1 diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1PreparedStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1PreparedStatement.java new file mode 100644 index 0000000000000000000000000000000000000000..b065b5722461557e454bc68746ed02ec94822614 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1PreparedStatement.java @@ -0,0 +1,14 @@ +package org.postgresql.jdbc1; + + +import java.sql.*; + +public class Jdbc1PreparedStatement extends AbstractJdbc1Statement implements PreparedStatement +{ + + public Jdbc1PreparedStatement(Jdbc1Connection connection, String sql) throws SQLException + { + super(connection, sql); + } + +} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Statement.java index bb073eae99871cdfaae4186c57815a14e14efaf9..f56841b3df8a0ff24b1f30d052a12f57eb765d33 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Statement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Statement.java @@ -3,7 +3,7 @@ package org.postgresql.jdbc1; import java.sql.*; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1Statement.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1Statement.java,v 1.2 2002/07/24 22:08:40 barry Exp $ * This class implements the java.sql.Statement interface for JDBC1. * However most of the implementation is really done in * org.postgresql.jdbc1.AbstractJdbc1Statement @@ -13,7 +13,7 @@ public class Jdbc1Statement extends org.postgresql.jdbc1.AbstractJdbc1Statement public Jdbc1Statement (Jdbc1Connection c) { - connection = c; + super(c); } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java deleted file mode 100644 index 9ef9a4289e59f816e05289b4ed642a68e54a7493..0000000000000000000000000000000000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java +++ /dev/null @@ -1,843 +0,0 @@ -package org.postgresql.jdbc1; - -// IMPORTANT NOTE: This file implements the JDBC 1 version of the driver. -// If you make any modifications to this file, you must make sure that the -// changes are also made (if relevent) to the related JDBC 2 class in the -// org.postgresql.jdbc2 package. - -import java.io.*; -import java.math.*; -import java.sql.*; -import java.text.*; -import java.util.*; -import org.postgresql.largeobject.*; -import org.postgresql.util.*; - -/* - * A SQL Statement is pre-compiled and stored in a PreparedStatement object. - * This object can then be used to efficiently execute this statement multiple - * times. - * - * <p><B>Note:</B> The setXXX methods for setting IN parameter values must - * specify types that are compatible with the defined SQL type of the input - * parameter. For instance, if the IN parameter has SQL type Integer, then - * setInt should be used. - * - * <p>If arbitrary parameter type conversions are required, then the setObject - * method should be used with a target SQL type. - * - * @see ResultSet - * @see java.sql.PreparedStatement - */ -public class PreparedStatement extends Jdbc1Statement implements java.sql.PreparedStatement -{ - String sql; - String[] templateStrings; - String[] inStrings; - Jdbc1Connection connection; - - // Some performance caches - private StringBuffer sbuf = new StringBuffer(); - - /* - * Constructor for the PreparedStatement class. - * Split the SQL statement into segments - separated by the arguments. - * When we rebuild the thing with the arguments, we can substitute the - * args and join the whole thing together. - * - * @param conn the instanatiating connection - * @param sql the SQL statement with ? for IN markers - * @exception SQLException if something bad occurs - */ - public PreparedStatement(Jdbc1Connection connection, String sql) throws SQLException - { - super(connection); - - Vector v = new Vector(); - boolean inQuotes = false; - int lastParmEnd = 0, i; - - this.sql = sql; - this.connection = connection; - for (i = 0; i < sql.length(); ++i) - { - int c = sql.charAt(i); - - if (c == '\'') - inQuotes = !inQuotes; - if (c == '?' && !inQuotes) - { - v.addElement(sql.substring (lastParmEnd, i)); - lastParmEnd = i + 1; - } - } - v.addElement(sql.substring (lastParmEnd, sql.length())); - - templateStrings = new String[v.size()]; - inStrings = new String[v.size() - 1]; - clearParameters(); - - for (i = 0 ; i < templateStrings.length; ++i) - templateStrings[i] = (String)v.elementAt(i); - } - - /* - * A Prepared SQL query is executed and its ResultSet is returned - * - * @return a ResultSet that contains the data produced by the - * * query - never null - * @exception SQLException if a database access error occurs - */ - public java.sql.ResultSet executeQuery() throws SQLException - { - StringBuffer s = new StringBuffer(); - int i; - - for (i = 0 ; i < inStrings.length ; ++i) - { - if (inStrings[i] == null) - throw new PSQLException("postgresql.prep.param", new Integer(i + 1)); - s.append (templateStrings[i]); - s.append (inStrings[i]); - } - s.append(templateStrings[inStrings.length]); - return super.executeQuery(s.toString()); // in Statement class - } - - /* - * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, - * SQL statements that return nothing such as SQL DDL statements can - * be executed. - * - * @return either the row count for INSERT, UPDATE or DELETE; or - * * 0 for SQL statements that return nothing. - * @exception SQLException if a database access error occurs - */ - public int executeUpdate() throws SQLException - { - StringBuffer s = new StringBuffer(); - int i; - - for (i = 0 ; i < inStrings.length ; ++i) - { - if (inStrings[i] == null) - throw new PSQLException("postgresql.prep.param", new Integer(i + 1)); - s.append (templateStrings[i]); - s.append (inStrings[i]); - } - s.append(templateStrings[inStrings.length]); - return super.executeUpdate(s.toString()); // in Statement class - } - - /* - * Set a parameter to SQL NULL - * - * <p><B>Note:</B> You must specify the parameters SQL type (although - * PostgreSQL ignores it) - * - * @param parameterIndex the first parameter is 1, etc... - * @param sqlType the SQL type code defined in java.sql.Types - * @exception SQLException if a database access error occurs - */ - public void setNull(int parameterIndex, int sqlType) throws SQLException - { - set(parameterIndex, "null"); - } - - /* - * Set a parameter to a Java boolean value. The driver converts this - * to a SQL BIT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBoolean(int parameterIndex, boolean x) throws SQLException - { - set(parameterIndex, x ? "'t'" : "'f'"); - } - - /* - * Set a parameter to a Java byte value. The driver converts this to - * a SQL TINYINT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setByte(int parameterIndex, byte x) throws SQLException - { - set(parameterIndex, Integer.toString(x)); - } - - /* - * Set a parameter to a Java short value. The driver converts this - * to a SQL SMALLINT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setShort(int parameterIndex, short x) throws SQLException - { - set(parameterIndex, Integer.toString(x)); - } - - /* - * Set a parameter to a Java int value. The driver converts this to - * a SQL INTEGER value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setInt(int parameterIndex, int x) throws SQLException - { - set(parameterIndex, Integer.toString(x)); - } - - /* - * Set a parameter to a Java long value. The driver converts this to - * a SQL BIGINT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setLong(int parameterIndex, long x) throws SQLException - { - set(parameterIndex, Long.toString(x)); - } - - /* - * Set a parameter to a Java float value. The driver converts this - * to a SQL FLOAT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setFloat(int parameterIndex, float x) throws SQLException - { - set(parameterIndex, Float.toString(x)); - } - - /* - * Set a parameter to a Java double value. The driver converts this - * to a SQL DOUBLE value when it sends it to the database - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setDouble(int parameterIndex, double x) throws SQLException - { - set(parameterIndex, Double.toString(x)); - } - - /* - * Set a parameter to a java.lang.BigDecimal value. The driver - * converts this to a SQL NUMERIC value when it sends it to the - * database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException - { - if (x == null) - setNull(parameterIndex, Types.OTHER); - else - { - set(parameterIndex, x.toString()); - } - } - - /* - * Set a parameter to a Java String value. The driver converts this - * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments - * size relative to the driver's limits on VARCHARs) when it sends it - * to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setString(int parameterIndex, String x) throws SQLException - { - // if the passed string is null, then set this column to null - if (x == null) - setNull(parameterIndex, Types.OTHER); - else - { - StringBuffer b = new StringBuffer(); - int i; - - b.append('\''); - for (i = 0 ; i < x.length() ; ++i) - { - char c = x.charAt(i); - if (c == '\\' || c == '\'') - b.append((char)'\\'); - b.append(c); - } - b.append('\''); - set(parameterIndex, b.toString()); - } - } - - /* - * Set a parameter to a Java array of bytes. The driver converts this - * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's - * size relative to the driver's limits on VARBINARYs) when it sends - * it to the database. - * - * <p>Implementation note: - * <br>With org.postgresql, this creates a large object, and stores the - * objects oid in this column. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBytes(int parameterIndex, byte x[]) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports the bytea datatype for byte arrays - if (null == x) - { - setNull(parameterIndex, Types.OTHER); - } - else - { - setString(parameterIndex, PGbytea.toPGString(x)); - } - } - else - { - //Version 7.1 and earlier support done as LargeObjects - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - lob.write(x); - lob.close(); - setInt(parameterIndex, oid); - } - } - - /* - * Set a parameter to a java.sql.Date value. The driver converts this - * to a SQL DATE value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setDate(int parameterIndex, java.sql.Date x) throws SQLException - { - if (null == x) - { - setNull(parameterIndex, Types.OTHER); - } - else - { - set(parameterIndex, "'" + x.toString() + "'"); - } - } - - /* - * Set a parameter to a java.sql.Time value. The driver converts - * this to a SQL TIME value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1...)); - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setTime(int parameterIndex, Time x) throws SQLException - { - if (null == x) - { - setNull(parameterIndex, Types.OTHER); - } - else - { - set(parameterIndex, "'" + x.toString() + "'"); - } - } - - /* - * Set a parameter to a java.sql.Timestamp value. The driver converts - * this to a SQL TIMESTAMP value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException - { - if (null == x) - { - setNull(parameterIndex, Types.OTHER); - } - else - { - // Use the shared StringBuffer - synchronized (sbuf) - { - sbuf.setLength(0); - sbuf.append("'"); - //format the timestamp - //we do our own formating so that we can get a format - //that works with both timestamp with time zone and - //timestamp without time zone datatypes. - //The format is '2002-01-01 23:59:59.123456-0130' - //we need to include the local time and timezone offset - //so that timestamp without time zone works correctly - int l_year = x.getYear() + 1900; - sbuf.append(l_year); - sbuf.append('-'); - int l_month = x.getMonth() + 1; - if (l_month < 10) sbuf.append('0'); - sbuf.append(l_month); - sbuf.append('-'); - int l_day = x.getDate(); - if (l_day < 10) sbuf.append('0'); - sbuf.append(l_day); - sbuf.append(' '); - int l_hours = x.getHours(); - if (l_hours < 10) sbuf.append('0'); - sbuf.append(l_hours); - sbuf.append(':'); - int l_minutes = x.getMinutes(); - if (l_minutes < 10) sbuf.append('0'); - sbuf.append(l_minutes); - sbuf.append(':'); - int l_seconds = x.getSeconds(); - if (l_seconds < 10) sbuf.append('0'); - sbuf.append(l_seconds); - // Make decimal from nanos. - char[] l_decimal = {'0','0','0','0','0','0','0','0','0'}; - char[] l_nanos = Integer.toString(x.getNanos()).toCharArray(); - System.arraycopy(l_nanos, 0, l_decimal, l_decimal.length - l_nanos.length, l_nanos.length); - sbuf.append('.'); - if (connection.haveMinimumServerVersion("7.2")) { - sbuf.append(l_decimal,0,6); - } else { - // Because 7.1 include bug that "hh:mm:59.999" becomes "hh:mm:60.00". - sbuf.append(l_decimal,0,2); - } - //add timezone offset - int l_offset = -(x.getTimezoneOffset()); - int l_houros = l_offset/60; - if (l_houros >= 0) { - sbuf.append('+'); - } else { - sbuf.append('-'); - } - if (l_houros > -10 && l_houros < 10) sbuf.append('0'); - if (l_houros >= 0) { - sbuf.append(l_houros); - } else { - sbuf.append(-l_houros); - } - int l_minos = l_offset - (l_houros *60); - if (l_minos != 0) { - if (l_minos < 10) sbuf.append('0'); - sbuf.append(l_minos); - } - sbuf.append("'"); - set(parameterIndex, sbuf.toString()); - } - - } - } - - /* - * When a very large ASCII value is input to a LONGVARCHAR parameter, - * it may be more practical to send it via a java.io.InputStream. - * JDBC will read the data from the stream as needed, until it reaches - * end-of-file. The JDBC driver will do any necessary conversion from - * ASCII to the database char format. - * - * <P><B>Note:</B> This stream object can either be a standard Java - * stream object or your own subclass that implements the standard - * interface. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @param length the number of bytes in the stream - * @exception SQLException if a database access error occurs - */ - public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try - { - InputStreamReader l_inStream = new InputStreamReader(x, "ASCII"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars, 0, length); - setString(parameterIndex, new String(l_chars, 0, l_charsRead)); - } - catch (UnsupportedEncodingException l_uee) - { - throw new PSQLException("postgresql.unusual", l_uee); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", l_ioe); - } - } - else - { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - setBinaryStream(parameterIndex, x, length); - } - } - - /* - * When a very large Unicode value is input to a LONGVARCHAR parameter, - * it may be more practical to send it via a java.io.InputStream. - * JDBC will read the data from the stream as needed, until it reaches - * end-of-file. The JDBC driver will do any necessary conversion from - * UNICODE to the database char format. - * - * <P><B>Note:</B> This stream object can either be a standard Java - * stream object or your own subclass that implements the standard - * interface. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try - { - InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars, 0, length); - setString(parameterIndex, new String(l_chars, 0, l_charsRead)); - } - catch (UnsupportedEncodingException l_uee) - { - throw new PSQLException("postgresql.unusual", l_uee); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", l_ioe); - } - } - else - { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - setBinaryStream(parameterIndex, x, length); - } - } - - /* - * When a very large binary value is input to a LONGVARBINARY parameter, - * it may be more practical to send it via a java.io.InputStream. - * JDBC will read the data from the stream as needed, until it reaches - * end-of-file. - * - * <P><B>Note:</B> This stream object can either be a standard Java - * stream object or your own subclass that implements the standard - * interface. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports BinaryStream for for the PG bytea type - //As the spec/javadoc for this method indicate this is to be used for - //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate - //long binary datatype, but with toast the bytea datatype is capable of - //handling very large values. Thus the implementation ends up calling - //setBytes() since there is no current way to stream the value to the server - byte[] l_bytes = new byte[length]; - int l_bytesRead; - try - { - l_bytesRead = x.read(l_bytes, 0, length); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", l_ioe); - } - if (l_bytesRead == length) - { - setBytes(parameterIndex, l_bytes); - } - else - { - //the stream contained less data than they said - byte[] l_bytes2 = new byte[l_bytesRead]; - System.arraycopy(l_bytes, 0, l_bytes2, 0, l_bytesRead); - setBytes(parameterIndex, l_bytes2); - } - } - else - { - //Version 7.1 only supported streams for LargeObjects - //but the jdbc spec indicates that streams should be - //available for LONGVARBINARY instead - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - OutputStream los = lob.getOutputStream(); - try - { - // could be buffered, but then the OutputStream returned by LargeObject - // is buffered internally anyhow, so there would be no performance - // boost gained, if anything it would be worse! - int c = x.read(); - int p = 0; - while (c > -1 && p < length) - { - los.write(c); - c = x.read(); - p++; - } - los.close(); - } - catch (IOException se) - { - throw new PSQLException("postgresql.unusual", se); - } - // lob is closed by the stream so don't call lob.close() - setInt(parameterIndex, oid); - } - } - - /* - * In general, parameter values remain in force for repeated used of a - * Statement. Setting a parameter value automatically clears its - * previous value. However, in coms cases, it is useful to immediately - * release the resources used by the current parameter values; this - * can be done by calling clearParameters - * - * @exception SQLException if a database access error occurs - */ - public void clearParameters() throws SQLException - { - int i; - - for (i = 0 ; i < inStrings.length ; i++) - inStrings[i] = null; - } - - /* - * Set the value of a parameter using an object; use the java.lang - * equivalent objects for integral values. - * - * <P>The given Java object will be converted to the targetSqlType before - * being sent to the database. - * - * <P>note that this method may be used to pass database-specific - * abstract data types. This is done by using a Driver-specific - * Java type and using a targetSqlType of java.sql.Types.OTHER - * - * @param parameterIndex the first parameter is 1... - * @param x the object containing the input parameter value - * @param targetSqlType The SQL type to be send to the database - * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC - * * types this is the number of digits after the decimal. For - * * all other types this value will be ignored. - * @exception SQLException if a database access error occurs - */ - public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException - { - if (x == null) - { - setNull(parameterIndex, Types.OTHER); - return; - } - switch (targetSqlType) - { - case Types.TINYINT: - case Types.SMALLINT: - case Types.INTEGER: - case Types.BIGINT: - case Types.REAL: - case Types.FLOAT: - case Types.DOUBLE: - case Types.DECIMAL: - case Types.NUMERIC: - if (x instanceof Boolean) - set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0"); - else - set(parameterIndex, x.toString()); - break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - setString(parameterIndex, x.toString()); - break; - case Types.DATE: - setDate(parameterIndex, (java.sql.Date)x); - break; - case Types.TIME: - setTime(parameterIndex, (Time)x); - break; - case Types.TIMESTAMP: - setTimestamp(parameterIndex, (Timestamp)x); - break; - case Types.BIT: - if (x instanceof Boolean) - { - set(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE"); - } - else - { - throw new PSQLException("postgresql.prep.type"); - } - break; - case Types.BINARY: - case Types.VARBINARY: - setObject(parameterIndex, x); - break; - case Types.OTHER: - setString(parameterIndex, ((PGobject)x).getValue()); - break; - default: - throw new PSQLException("postgresql.prep.type"); - } - } - - public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException - { - setObject(parameterIndex, x, targetSqlType, 0); - } - - /* - * This stores an Object into a parameter. - * <p>New for 6.4, if the object is not recognised, but it is - * Serializable, then the object is serialised using the - * org.postgresql.util.Serialize class. - */ - public void setObject(int parameterIndex, Object x) throws SQLException - { - if (x == null) - { - setNull(parameterIndex, Types.OTHER); - return; - } - if (x instanceof String) - setString(parameterIndex, (String)x); - else if (x instanceof BigDecimal) - setBigDecimal(parameterIndex, (BigDecimal)x); - else if (x instanceof Short) - setShort(parameterIndex, ((Short)x).shortValue()); - else if (x instanceof Integer) - setInt(parameterIndex, ((Integer)x).intValue()); - else if (x instanceof Long) - setLong(parameterIndex, ((Long)x).longValue()); - else if (x instanceof Float) - setFloat(parameterIndex, ((Float)x).floatValue()); - else if (x instanceof Double) - setDouble(parameterIndex, ((Double)x).doubleValue()); - else if (x instanceof byte[]) - setBytes(parameterIndex, (byte[])x); - else if (x instanceof java.sql.Date) - setDate(parameterIndex, (java.sql.Date)x); - else if (x instanceof Time) - setTime(parameterIndex, (Time)x); - else if (x instanceof Timestamp) - setTimestamp(parameterIndex, (Timestamp)x); - else if (x instanceof Boolean) - setBoolean(parameterIndex, ((Boolean)x).booleanValue()); - else if (x instanceof PGobject) - setString(parameterIndex, ((PGobject)x).getValue()); - else - setLong(parameterIndex, connection.storeObject(x)); - } - - /* - * Some prepared statements return multiple results; the execute method - * handles these complex statements as well as the simpler form of - * statements handled by executeQuery and executeUpdate - * - * @return true if the next result is a ResultSet; false if it is an - * * update count or there are no more results - * @exception SQLException if a database access error occurs - */ - public boolean execute() throws SQLException - { - StringBuffer s = new StringBuffer(); - int i; - - for (i = 0 ; i < inStrings.length ; ++i) - { - if (inStrings[i] == null) - throw new PSQLException("postgresql.prep.param", new Integer(i + 1)); - s.append (templateStrings[i]); - s.append (inStrings[i]); - } - s.append(templateStrings[inStrings.length]); - return super.execute(s.toString()); // in Statement class - } - - /* - * Returns the SQL statement with the current template values - * substituted. - */ - public String toString() - { - StringBuffer s = new StringBuffer(); - int i; - - for (i = 0 ; i < inStrings.length ; ++i) - { - if (inStrings[i] == null) - s.append( '?' ); - else - s.append (templateStrings[i]); - s.append (inStrings[i]); - } - s.append(templateStrings[inStrings.length]); - return s.toString(); - } - - // ************************************************************** - // END OF PUBLIC INTERFACE - // ************************************************************** - - /* - * There are a lot of setXXX classes which all basically do - * the same thing. We need a method which actually does the - * set for us. - * - * @param paramIndex the index into the inString - * @param s a string to be stored - * @exception SQLException if something goes wrong - */ - private void set(int paramIndex, String s) throws SQLException - { - if (paramIndex < 1 || paramIndex > inStrings.length) - throw new PSQLException("postgresql.prep.range"); - inStrings[paramIndex - 1] = s; - } -} diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java index 50c5d942bbf63ce71e53e46d756bf8c681b6d123..d2c5ee07607a39dd84f1fa141f0f5f77d81a54f2 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java @@ -13,14 +13,14 @@ import org.postgresql.largeobject.*; import org.postgresql.util.PGbytea; import org.postgresql.util.PSQLException; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.2 2002/07/24 22:08:42 barry Exp $ * This class defines methods of the jdbc2 specification. This class extends * org.postgresql.jdbc1.AbstractJdbc1ResultSet which provides the jdbc1 * methods. The real Statement class (for jdbc2) is org.postgresql.jdbc2.Jdbc2ResultSet */ public class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet { - protected Jdbc2Statement statement; + protected Statement statement; protected String sqlQuery=null; @@ -373,7 +373,7 @@ public class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.AbstractJdbc1Re } // This one needs some thought, as not all ResultSets come from a statement - public java.sql.Statement getStatement() throws SQLException + public Statement getStatement() throws SQLException { return statement; } @@ -740,7 +740,7 @@ public class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.AbstractJdbc1Re * It's used currently by getStatement() but may also with the new core * package. */ - public void setStatement(Jdbc2Statement statement) + public void setStatement(Statement statement) { this.statement = statement; } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java index 3d6f6553ced3fea5afba83d7e4c81cbbcabf5d1d..47c2c77983153637d94df8a2c0e5f714de6fcc81 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java @@ -1,11 +1,13 @@ package org.postgresql.jdbc2; +import java.io.*; import java.sql.*; import java.util.Vector; +import org.postgresql.largeobject.*; import org.postgresql.util.PSQLException; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2Statement.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2Statement.java,v 1.2 2002/07/24 22:08:42 barry Exp $ * This class defines methods of the jdbc2 specification. This class extends * org.postgresql.jdbc1.AbstractJdbc1Statement which provides the jdbc1 * methods. The real Statement class (for jdbc2) is org.postgresql.jdbc2.Jdbc2Statement @@ -17,6 +19,18 @@ public abstract class AbstractJdbc2Statement extends org.postgresql.jdbc1.Abstra protected int resultsettype; // the resultset type to return protected int concurrency; // is it updateable or not? + public AbstractJdbc2Statement (AbstractJdbc2Connection c) + { + super(c); + resultsettype = java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; + concurrency = java.sql.ResultSet.CONCUR_READ_ONLY; + } + + public AbstractJdbc2Statement(AbstractJdbc2Connection connection, String sql) throws SQLException + { + super(connection, sql); + } + /* * Execute a SQL statement that may return multiple results. We * don't have to worry about this since we do not support multiple @@ -31,10 +45,9 @@ public abstract class AbstractJdbc2Statement extends org.postgresql.jdbc1.Abstra public boolean execute(String sql) throws SQLException { boolean l_return = super.execute(sql); - //Now do the jdbc2 specific stuff //required for ResultSet.getStatement() to work - ((AbstractJdbc2ResultSet)result).setStatement((Jdbc2Statement)this); + ((AbstractJdbc2ResultSet)result).setStatement((Statement)this); // Added this so that the Updateable resultset knows the query that gave this ((AbstractJdbc2ResultSet)result).setSQLQuery(sql); @@ -139,4 +152,183 @@ public abstract class AbstractJdbc2Statement extends org.postgresql.jdbc1.Abstra resultsettype = value; } + public void addBatch() throws SQLException + { + addBatch(compileQuery()); + } + + public java.sql.ResultSetMetaData getMetaData() throws SQLException + { + java.sql.ResultSet rs = getResultSet(); + if (rs != null) + return rs.getMetaData(); + + // Does anyone really know what this method does? + return null; + } + + public void setArray(int i, java.sql.Array x) throws SQLException + { + setString(i, x.toString()); + } + + public void setBlob(int i, Blob x) throws SQLException + { + InputStream l_inStream = x.getBinaryStream(); + int l_length = (int) x.length(); + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + OutputStream los = lob.getOutputStream(); + try + { + // could be buffered, but then the OutputStream returned by LargeObject + // is buffered internally anyhow, so there would be no performance + // boost gained, if anything it would be worse! + int c = l_inStream.read(); + int p = 0; + while (c > -1 && p < l_length) + { + los.write(c); + c = l_inStream.read(); + p++; + } + los.close(); + } + catch (IOException se) + { + throw new PSQLException("postgresql.unusual", se); + } + // lob is closed by the stream so don't call lob.close() + setInt(i, oid); + } + + public void setCharacterStream(int i, java.io.Reader x, int length) throws SQLException + { + if (connection.haveMinimumCompatibleVersion("7.2")) + { + //Version 7.2 supports CharacterStream for for the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long varchar datatype, but with toast all the text datatypes are capable of + //handling very large values. Thus the implementation ends up calling + //setString() since there is no current way to stream the value to the server + char[] l_chars = new char[length]; + int l_charsRead; + try + { + l_charsRead = x.read(l_chars, 0, length); + } + catch (IOException l_ioe) + { + throw new PSQLException("postgresql.unusual", l_ioe); + } + setString(i, new String(l_chars, 0, l_charsRead)); + } + else + { + //Version 7.1 only supported streams for LargeObjects + //but the jdbc spec indicates that streams should be + //available for LONGVARCHAR instead + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + OutputStream los = lob.getOutputStream(); + try + { + // could be buffered, but then the OutputStream returned by LargeObject + // is buffered internally anyhow, so there would be no performance + // boost gained, if anything it would be worse! + int c = x.read(); + int p = 0; + while (c > -1 && p < length) + { + los.write(c); + c = x.read(); + p++; + } + los.close(); + } + catch (IOException se) + { + throw new PSQLException("postgresql.unusual", se); + } + // lob is closed by the stream so don't call lob.close() + setInt(i, oid); + } + } + + public void setClob(int i, Clob x) throws SQLException + { + InputStream l_inStream = x.getAsciiStream(); + int l_length = (int) x.length(); + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + OutputStream los = lob.getOutputStream(); + try + { + // could be buffered, but then the OutputStream returned by LargeObject + // is buffered internally anyhow, so there would be no performance + // boost gained, if anything it would be worse! + int c = l_inStream.read(); + int p = 0; + while (c > -1 && p < l_length) + { + los.write(c); + c = l_inStream.read(); + p++; + } + los.close(); + } + catch (IOException se) + { + throw new PSQLException("postgresql.unusual", se); + } + // lob is closed by the stream so don't call lob.close() + setInt(i, oid); + } + + public void setNull(int i, int t, String s) throws SQLException + { + setNull(i, t); + } + + public void setRef(int i, Ref x) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public void setDate(int i, java.sql.Date d, java.util.Calendar cal) throws SQLException + { + if (cal == null) + setDate(i, d); + else + { + cal.setTime(d); + setDate(i, new java.sql.Date(cal.getTime().getTime())); + } + } + + public void setTime(int i, Time t, java.util.Calendar cal) throws SQLException + { + if (cal == null) + setTime(i, t); + else + { + cal.setTime(t); + setTime(i, new java.sql.Time(cal.getTime().getTime())); + } + } + + public void setTimestamp(int i, Timestamp t, java.util.Calendar cal) throws SQLException + { + if (cal == null) + setTimestamp(i, t); + else + { + cal.setTime(t); + setTimestamp(i, new java.sql.Timestamp(cal.getTime().getTime())); + } + } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java index 9d37bf04bc50c42cacc133b394cb719e3a1f6289..51dd9b2c7f0138b26f65e0eda732e2b976d51441 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java @@ -40,7 +40,7 @@ import org.postgresql.util.*; * @author Paul Bethe (implementer) */ -public class CallableStatement extends org.postgresql.jdbc2.PreparedStatement implements java.sql.CallableStatement +public class CallableStatement extends org.postgresql.jdbc2.Jdbc2PreparedStatement implements java.sql.CallableStatement { /* * @exception SQLException on failure diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java index cfbb3486ec6f6d25dc6b0ca25110bedf73a6e65e..787b14e62af6485d84fb46c398b9327521f26420 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java @@ -5,7 +5,7 @@ import java.sql.*; import java.util.Vector; import org.postgresql.Field; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2Connection.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2Connection.java,v 1.2 2002/07/24 22:08:42 barry Exp $ * This class implements the java.sql.Connection interface for JDBC2. * However most of the implementation is really done in * org.postgresql.jdbc2.AbstractJdbc2Connection or one of it's parents @@ -24,7 +24,7 @@ public class Jdbc2Connection extends org.postgresql.jdbc2.AbstractJdbc2Connectio public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - org.postgresql.jdbc2.PreparedStatement s = new org.postgresql.jdbc2.PreparedStatement(this, sql); + Jdbc2PreparedStatement s = new Jdbc2PreparedStatement(this, sql); s.setResultSetType(resultSetType); s.setResultSetConcurrency(resultSetConcurrency); return s; diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2PreparedStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2PreparedStatement.java new file mode 100644 index 0000000000000000000000000000000000000000..0472007d3dd91cea4f851913fb9d022c2176bb61 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2PreparedStatement.java @@ -0,0 +1,15 @@ +package org.postgresql.jdbc2; + + +import java.sql.*; + +public class Jdbc2PreparedStatement extends AbstractJdbc2Statement implements java.sql.PreparedStatement +{ + + public Jdbc2PreparedStatement(Jdbc2Connection connection, String sql) throws SQLException + { + super(connection, sql); + } + +} + diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Statement.java index 31cec93821a4f8dde7b01b811a3d584c5e579246..4f63d10c14f485caa190501568b4812dcd533ca3 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Statement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Statement.java @@ -3,7 +3,7 @@ package org.postgresql.jdbc2; import java.sql.*; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2Statement.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2Statement.java,v 1.2 2002/07/24 22:08:43 barry Exp $ * This class implements the java.sql.Statement interface for JDBC2. * However most of the implementation is really done in * org.postgresql.jdbc2.AbstractJdbc2Statement or one of it's parents @@ -13,9 +13,7 @@ public class Jdbc2Statement extends org.postgresql.jdbc2.AbstractJdbc2Statement public Jdbc2Statement (Jdbc2Connection c) { - connection = c; - resultsettype = java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; - concurrency = java.sql.ResultSet.CONCUR_READ_ONLY; + super(c); } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java deleted file mode 100644 index 21aba8d9ee089ff84ad991c7873d25f6e66a7bec..0000000000000000000000000000000000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java +++ /dev/null @@ -1,1086 +0,0 @@ -package org.postgresql.jdbc2; - -// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver. -// If you make any modifications to this file, you must make sure that the -// changes are also made (if relevent) to the related JDBC 1 class in the -// org.postgresql.jdbc1 package. - -import java.io.*; -import java.math.*; -import java.sql.*; -import java.text.*; -import java.util.*; -import org.postgresql.largeobject.*; -import org.postgresql.util.*; - -/* - * A SQL Statement is pre-compiled and stored in a PreparedStatement object. - * This object can then be used to efficiently execute this statement multiple - * times. - * - * <p><B>Note:</B> The setXXX methods for setting IN parameter values must - * specify types that are compatible with the defined SQL type of the input - * parameter. For instance, if the IN parameter has SQL type Integer, then - * setInt should be used. - * - * <p>If arbitrary parameter type conversions are required, then the setObject - * method should be used with a target SQL type. - * - * @see ResultSet - * @see java.sql.PreparedStatement - */ -public class PreparedStatement extends Jdbc2Statement implements java.sql.PreparedStatement -{ - String sql; - String[] templateStrings; - String[] inStrings; - Jdbc2Connection connection; - - // Some performance caches - private StringBuffer sbuf = new StringBuffer(); - - /* - * Constructor for the PreparedStatement class. - * Split the SQL statement into segments - separated by the arguments. - * When we rebuild the thing with the arguments, we can substitute the - * args and join the whole thing together. - * - * @param conn the instanatiating connection - * @param sql the SQL statement with ? for IN markers - * @exception SQLException if something bad occurs - */ - public PreparedStatement(Jdbc2Connection connection, String sql) throws SQLException - { - super(connection); - - this.sql = sql; - this.connection = connection; - parseSqlStmt (); // this allows Callable stmt to override - } - - protected void parseSqlStmt () throws SQLException { - Vector v = new Vector(); - boolean inQuotes = false; - int lastParmEnd = 0, i; - - for (i = 0; i < sql.length(); ++i) - { - int c = sql.charAt(i); - - if (c == '\'') - inQuotes = !inQuotes; - if (c == '?' && !inQuotes) - { - v.addElement(sql.substring (lastParmEnd, i)); - lastParmEnd = i + 1; - } - } - v.addElement(sql.substring (lastParmEnd, sql.length())); - - templateStrings = new String[v.size()]; - inStrings = new String[v.size() - 1]; - clearParameters(); - - for (i = 0 ; i < templateStrings.length; ++i) - templateStrings[i] = (String)v.elementAt(i); - } - - /* - * A Prepared SQL query is executed and its ResultSet is returned - * - * @return a ResultSet that contains the data produced by the - * * query - never null - * @exception SQLException if a database access error occurs - */ - public java.sql.ResultSet executeQuery() throws SQLException - { - return super.executeQuery(compileQuery()); // in Statement class - } - - /* - * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, - * SQL statements that return nothing such as SQL DDL statements can - * be executed. - * - * @return either the row count for INSERT, UPDATE or DELETE; or - * * 0 for SQL statements that return nothing. - * @exception SQLException if a database access error occurs - */ - public int executeUpdate() throws SQLException - { - return super.executeUpdate(compileQuery()); // in Statement class - } - - /* - * Helper - this compiles the SQL query from the various parameters - * This is identical to toString() except it throws an exception if a - * parameter is unused. - */ - protected synchronized String compileQuery() - throws SQLException - { - sbuf.setLength(0); - int i; - - for (i = 0 ; i < inStrings.length ; ++i) - { - if (inStrings[i] == null) - throw new PSQLException("postgresql.prep.param", new Integer(i + 1)); - sbuf.append (templateStrings[i]).append (inStrings[i]); - } - sbuf.append(templateStrings[inStrings.length]); - return sbuf.toString(); - } - - /* - * Set a parameter to SQL NULL - * - * <p><B>Note:</B> You must specify the parameters SQL type (although - * PostgreSQL ignores it) - * - * @param parameterIndex the first parameter is 1, etc... - * @param sqlType the SQL type code defined in java.sql.Types - * @exception SQLException if a database access error occurs - */ - public void setNull(int parameterIndex, int sqlType) throws SQLException - { - set(parameterIndex, "null"); - } - - /* - * Set a parameter to a Java boolean value. The driver converts this - * to a SQL BIT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBoolean(int parameterIndex, boolean x) throws SQLException - { - set(parameterIndex, x ? "'t'" : "'f'"); - } - - /* - * Set a parameter to a Java byte value. The driver converts this to - * a SQL TINYINT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setByte(int parameterIndex, byte x) throws SQLException - { - set(parameterIndex, Integer.toString(x)); - } - - /* - * Set a parameter to a Java short value. The driver converts this - * to a SQL SMALLINT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setShort(int parameterIndex, short x) throws SQLException - { - set(parameterIndex, Integer.toString(x)); - } - - /* - * Set a parameter to a Java int value. The driver converts this to - * a SQL INTEGER value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setInt(int parameterIndex, int x) throws SQLException - { - set(parameterIndex, Integer.toString(x)); - } - - /* - * Set a parameter to a Java long value. The driver converts this to - * a SQL BIGINT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setLong(int parameterIndex, long x) throws SQLException - { - set(parameterIndex, Long.toString(x)); - } - - /* - * Set a parameter to a Java float value. The driver converts this - * to a SQL FLOAT value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setFloat(int parameterIndex, float x) throws SQLException - { - set(parameterIndex, Float.toString(x)); - } - - /* - * Set a parameter to a Java double value. The driver converts this - * to a SQL DOUBLE value when it sends it to the database - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setDouble(int parameterIndex, double x) throws SQLException - { - set(parameterIndex, Double.toString(x)); - } - - /* - * Set a parameter to a java.lang.BigDecimal value. The driver - * converts this to a SQL NUMERIC value when it sends it to the - * database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException - { - if (x == null) { - setNull(parameterIndex, Types.OTHER); - } else { - set(parameterIndex, x.toString()); - } - } - - /* - * Set a parameter to a Java String value. The driver converts this - * to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments - * size relative to the driver's limits on VARCHARs) when it sends it - * to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setString(int parameterIndex, String x) throws SQLException - { - // if the passed string is null, then set this column to null - if (x == null) - setNull(parameterIndex, Types.OTHER); - else - { - // use the shared buffer object. Should never clash but this makes - // us thread safe! - synchronized (sbuf) - { - sbuf.setLength(0); - int i; - - sbuf.append('\''); - for (i = 0 ; i < x.length() ; ++i) - { - char c = x.charAt(i); - if (c == '\\' || c == '\'') - sbuf.append((char)'\\'); - sbuf.append(c); - } - sbuf.append('\''); - set(parameterIndex, sbuf.toString()); - } - } - } - - /* - * Set a parameter to a Java array of bytes. The driver converts this - * to a SQL VARBINARY or LONGVARBINARY (depending on the argument's - * size relative to the driver's limits on VARBINARYs) when it sends - * it to the database. - * - * <p>Implementation note: - * <br>With org.postgresql, this creates a large object, and stores the - * objects oid in this column. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBytes(int parameterIndex, byte x[]) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports the bytea datatype for byte arrays - if (null == x) - { - setNull(parameterIndex, Types.OTHER); - } - else - { - setString(parameterIndex, PGbytea.toPGString(x)); - } - } - else - { - //Version 7.1 and earlier support done as LargeObjects - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - lob.write(x); - lob.close(); - setInt(parameterIndex, oid); - } - } - - /* - * Set a parameter to a java.sql.Date value. The driver converts this - * to a SQL DATE value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setDate(int parameterIndex, java.sql.Date x) throws SQLException - { - if (null == x) - { - setNull(parameterIndex, Types.OTHER); - } - else - { - set(parameterIndex, "'" + x.toString() + "'"); - } - } - - /* - * Set a parameter to a java.sql.Time value. The driver converts - * this to a SQL TIME value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1...)); - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setTime(int parameterIndex, Time x) throws SQLException - { - if (null == x) - { - setNull(parameterIndex, Types.OTHER); - } - else - { - set(parameterIndex, "'" + x.toString() + "'"); - } - } - - /* - * Set a parameter to a java.sql.Timestamp value. The driver converts - * this to a SQL TIMESTAMP value when it sends it to the database. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException - { - if (null == x) - { - setNull(parameterIndex, Types.OTHER); - } - else - { - // Use the shared StringBuffer - synchronized (sbuf) - { - sbuf.setLength(0); - sbuf.append("'"); - //format the timestamp - //we do our own formating so that we can get a format - //that works with both timestamp with time zone and - //timestamp without time zone datatypes. - //The format is '2002-01-01 23:59:59.123456-0130' - //we need to include the local time and timezone offset - //so that timestamp without time zone works correctly - int l_year = x.getYear() + 1900; - sbuf.append(l_year); - sbuf.append('-'); - int l_month = x.getMonth() + 1; - if (l_month < 10) sbuf.append('0'); - sbuf.append(l_month); - sbuf.append('-'); - int l_day = x.getDate(); - if (l_day < 10) sbuf.append('0'); - sbuf.append(l_day); - sbuf.append(' '); - int l_hours = x.getHours(); - if (l_hours < 10) sbuf.append('0'); - sbuf.append(l_hours); - sbuf.append(':'); - int l_minutes = x.getMinutes(); - if (l_minutes < 10) sbuf.append('0'); - sbuf.append(l_minutes); - sbuf.append(':'); - int l_seconds = x.getSeconds(); - if (l_seconds < 10) sbuf.append('0'); - sbuf.append(l_seconds); - // Make decimal from nanos. - char[] l_decimal = {'0','0','0','0','0','0','0','0','0'}; - char[] l_nanos = Integer.toString(x.getNanos()).toCharArray(); - System.arraycopy(l_nanos, 0, l_decimal, l_decimal.length - l_nanos.length, l_nanos.length); - sbuf.append('.'); - if (connection.haveMinimumServerVersion("7.2")) { - sbuf.append(l_decimal,0,6); - } else { - // Because 7.1 include bug that "hh:mm:59.999" becomes "hh:mm:60.00". - sbuf.append(l_decimal,0,2); - } - //add timezone offset - int l_offset = -(x.getTimezoneOffset()); - int l_houros = l_offset/60; - if (l_houros >= 0) { - sbuf.append('+'); - } else { - sbuf.append('-'); - } - if (l_houros > -10 && l_houros < 10) sbuf.append('0'); - if (l_houros >= 0) { - sbuf.append(l_houros); - } else { - sbuf.append(-l_houros); - } - int l_minos = l_offset - (l_houros *60); - if (l_minos != 0) { - if (l_minos < 10) sbuf.append('0'); - sbuf.append(l_minos); - } - sbuf.append("'"); - set(parameterIndex, sbuf.toString()); - } - - } - } - - /* - * When a very large ASCII value is input to a LONGVARCHAR parameter, - * it may be more practical to send it via a java.io.InputStream. - * JDBC will read the data from the stream as needed, until it reaches - * end-of-file. The JDBC driver will do any necessary conversion from - * ASCII to the database char format. - * - * <P><B>Note:</B> This stream object can either be a standard Java - * stream object or your own subclass that implements the standard - * interface. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @param length the number of bytes in the stream - * @exception SQLException if a database access error occurs - */ - public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try - { - InputStreamReader l_inStream = new InputStreamReader(x, "ASCII"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars, 0, length); - setString(parameterIndex, new String(l_chars, 0, l_charsRead)); - } - catch (UnsupportedEncodingException l_uee) - { - throw new PSQLException("postgresql.unusual", l_uee); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", l_ioe); - } - } - else - { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - setBinaryStream(parameterIndex, x, length); - } - } - - /* - * When a very large Unicode value is input to a LONGVARCHAR parameter, - * it may be more practical to send it via a java.io.InputStream. - * JDBC will read the data from the stream as needed, until it reaches - * end-of-file. The JDBC driver will do any necessary conversion from - * UNICODE to the database char format. - * - * ** DEPRECIATED IN JDBC 2 ** - * - * <P><B>Note:</B> This stream object can either be a standard Java - * stream object or your own subclass that implements the standard - * interface. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - * @deprecated - */ - public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text) - //As the spec/javadoc for this method indicate this is to be used for - //large String values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - try - { - InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8"); - char[] l_chars = new char[length]; - int l_charsRead = l_inStream.read(l_chars, 0, length); - setString(parameterIndex, new String(l_chars, 0, l_charsRead)); - } - catch (UnsupportedEncodingException l_uee) - { - throw new PSQLException("postgresql.unusual", l_uee); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", l_ioe); - } - } - else - { - //Version 7.1 supported only LargeObjects by treating everything - //as binary data - setBinaryStream(parameterIndex, x, length); - } - } - - /* - * When a very large binary value is input to a LONGVARBINARY parameter, - * it may be more practical to send it via a java.io.InputStream. - * JDBC will read the data from the stream as needed, until it reaches - * end-of-file. - * - * <P><B>Note:</B> This stream object can either be a standard Java - * stream object or your own subclass that implements the standard - * interface. - * - * @param parameterIndex the first parameter is 1... - * @param x the parameter value - * @exception SQLException if a database access error occurs - */ - public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports BinaryStream for for the PG bytea type - //As the spec/javadoc for this method indicate this is to be used for - //large binary values (i.e. LONGVARBINARY) PG doesn't have a separate - //long binary datatype, but with toast the bytea datatype is capable of - //handling very large values. Thus the implementation ends up calling - //setBytes() since there is no current way to stream the value to the server - byte[] l_bytes = new byte[length]; - int l_bytesRead; - try - { - l_bytesRead = x.read(l_bytes, 0, length); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", l_ioe); - } - if (l_bytesRead == length) - { - setBytes(parameterIndex, l_bytes); - } - else - { - //the stream contained less data than they said - byte[] l_bytes2 = new byte[l_bytesRead]; - System.arraycopy(l_bytes, 0, l_bytes2, 0, l_bytesRead); - setBytes(parameterIndex, l_bytes2); - } - } - else - { - //Version 7.1 only supported streams for LargeObjects - //but the jdbc spec indicates that streams should be - //available for LONGVARBINARY instead - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - OutputStream los = lob.getOutputStream(); - try - { - // could be buffered, but then the OutputStream returned by LargeObject - // is buffered internally anyhow, so there would be no performance - // boost gained, if anything it would be worse! - int c = x.read(); - int p = 0; - while (c > -1 && p < length) - { - los.write(c); - c = x.read(); - p++; - } - los.close(); - } - catch (IOException se) - { - throw new PSQLException("postgresql.unusual", se); - } - // lob is closed by the stream so don't call lob.close() - setInt(parameterIndex, oid); - } - } - - /* - * In general, parameter values remain in force for repeated used of a - * Statement. Setting a parameter value automatically clears its - * previous value. However, in coms cases, it is useful to immediately - * release the resources used by the current parameter values; this - * can be done by calling clearParameters - * - * @exception SQLException if a database access error occurs - */ - public void clearParameters() throws SQLException - { - int i; - - for (i = 0 ; i < inStrings.length ; i++) - inStrings[i] = null; - } - - /* - * Set the value of a parameter using an object; use the java.lang - * equivalent objects for integral values. - * - * <P>The given Java object will be converted to the targetSqlType before - * being sent to the database. - * - * <P>note that this method may be used to pass database-specific - * abstract data types. This is done by using a Driver-specific - * Java type and using a targetSqlType of java.sql.Types.OTHER - * - * @param parameterIndex the first parameter is 1... - * @param x the object containing the input parameter value - * @param targetSqlType The SQL type to be send to the database - * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC - * types this is the number of digits after the decimal. For - * all other types this value will be ignored. - * @exception SQLException if a database access error occurs - */ - public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException - { - if (x == null) - { - setNull(parameterIndex, Types.OTHER); - return; - } - switch (targetSqlType) - { - case Types.TINYINT: - case Types.SMALLINT: - case Types.INTEGER: - case Types.BIGINT: - case Types.REAL: - case Types.FLOAT: - case Types.DOUBLE: - case Types.DECIMAL: - case Types.NUMERIC: - if (x instanceof Boolean) - set(parameterIndex, ((Boolean)x).booleanValue() ? "1" : "0"); - else - set(parameterIndex, x.toString()); - break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - setString(parameterIndex, x.toString()); - break; - case Types.DATE: - setDate(parameterIndex, (java.sql.Date)x); - break; - case Types.TIME: - setTime(parameterIndex, (Time)x); - break; - case Types.TIMESTAMP: - setTimestamp(parameterIndex, (Timestamp)x); - break; - case Types.BIT: - if (x instanceof Boolean) - { - set(parameterIndex, ((Boolean)x).booleanValue() ? "TRUE" : "FALSE"); - } - else - { - throw new PSQLException("postgresql.prep.type"); - } - break; - case Types.BINARY: - case Types.VARBINARY: - setObject(parameterIndex, x); - break; - case Types.OTHER: - setString(parameterIndex, ((PGobject)x).getValue()); - break; - default: - throw new PSQLException("postgresql.prep.type"); - } - } - - public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException - { - setObject(parameterIndex, x, targetSqlType, 0); - } - - /* - * This stores an Object into a parameter. - * <p>New for 6.4, if the object is not recognised, but it is - * Serializable, then the object is serialised using the - * org.postgresql.util.Serialize class. - */ - public void setObject(int parameterIndex, Object x) throws SQLException - { - if (x == null) - { - setNull(parameterIndex, Types.OTHER); - return; - } - if (x instanceof String) - setString(parameterIndex, (String)x); - else if (x instanceof BigDecimal) - setBigDecimal(parameterIndex, (BigDecimal)x); - else if (x instanceof Short) - setShort(parameterIndex, ((Short)x).shortValue()); - else if (x instanceof Integer) - setInt(parameterIndex, ((Integer)x).intValue()); - else if (x instanceof Long) - setLong(parameterIndex, ((Long)x).longValue()); - else if (x instanceof Float) - setFloat(parameterIndex, ((Float)x).floatValue()); - else if (x instanceof Double) - setDouble(parameterIndex, ((Double)x).doubleValue()); - else if (x instanceof byte[]) - setBytes(parameterIndex, (byte[])x); - else if (x instanceof java.sql.Date) - setDate(parameterIndex, (java.sql.Date)x); - else if (x instanceof Time) - setTime(parameterIndex, (Time)x); - else if (x instanceof Timestamp) - setTimestamp(parameterIndex, (Timestamp)x); - else if (x instanceof Boolean) - setBoolean(parameterIndex, ((Boolean)x).booleanValue()); - else if (x instanceof PGobject) - setString(parameterIndex, ((PGobject)x).getValue()); - else - // Try to store java object in database - setSerialize(parameterIndex, connection.storeObject(x), x.getClass().getName() ); - } - - /* - * Some prepared statements return multiple results; the execute method - * handles these complex statements as well as the simpler form of - * statements handled by executeQuery and executeUpdate - * - * @return true if the next result is a ResultSet; false if it is an - * update count or there are no more results - * @exception SQLException if a database access error occurs - */ - public boolean execute() throws SQLException - { - return super.execute(compileQuery()); // in Statement class - } - - /* - * Returns the SQL statement with the current template values - * substituted. - * NB: This is identical to compileQuery() except instead of throwing - * SQLException if a parameter is null, it places ? instead. - */ - public String toString() - { - synchronized (sbuf) - { - sbuf.setLength(0); - int i; - - for (i = 0 ; i < inStrings.length ; ++i) - { - if (inStrings[i] == null) - sbuf.append( '?' ); - else - sbuf.append (templateStrings[i]); - sbuf.append (inStrings[i]); - } - sbuf.append(templateStrings[inStrings.length]); - return sbuf.toString(); - } - } - - // ************************************************************** - // END OF PUBLIC INTERFACE - // ************************************************************** - - /* - * There are a lot of setXXX classes which all basically do - * the same thing. We need a method which actually does the - * set for us. - * - * @param paramIndex the index into the inString - * @param s a string to be stored - * @exception SQLException if something goes wrong - */ - protected void set(int paramIndex, String s) throws SQLException - { - if (paramIndex < 1 || paramIndex > inStrings.length) - throw new PSQLException("postgresql.prep.range"); - inStrings[paramIndex - 1] = s; - } - - /* - * Set a parameter to a tablerow-type oid reference. - * - * @param parameterIndex the first parameter is 1... - * @param x the oid of the object from org.postgresql.util.Serialize.store - * @param classname the classname of the java object x - * @exception SQLException if a database access error occurs - */ - private void setSerialize(int parameterIndex, long x, String classname) throws SQLException - { - // converts . to _, toLowerCase, and ensures length<32 - String tablename = Serialize.toPostgreSQL( classname ); - DriverManager.println("setSerialize: setting " + x + "::" + tablename ); - - // OID reference to tablerow-type must be cast like: <oid>::<tablename> - // Note that postgres support for tablerow data types is incomplete/broken. - // This cannot be just a plain OID because then there would be ambiguity - // between when you want the oid itself and when you want the object - // an oid references. - set(parameterIndex, Long.toString(x) + "::" + tablename ); - } - - - // ** JDBC 2 Extensions ** - - /* - * This parses the query and adds it to the current batch - */ - public void addBatch() throws SQLException - { - super.addBatch(compileQuery()); - } - - /* - * Not sure what this one does, so I'm saying this returns the MetaData for - * the last ResultSet returned! - */ - public java.sql.ResultSetMetaData getMetaData() throws SQLException - { - java.sql.ResultSet rs = getResultSet(); - if (rs != null) - return rs.getMetaData(); - - // Does anyone really know what this method does? - return null; - } - - public void setArray(int i, java.sql.Array x) throws SQLException - { - setString(i, x.toString()); - } - - /* - * Sets a Blob - */ - public void setBlob(int i, Blob x) throws SQLException - { - InputStream l_inStream = x.getBinaryStream(); - int l_length = (int) x.length(); - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - OutputStream los = lob.getOutputStream(); - try - { - // could be buffered, but then the OutputStream returned by LargeObject - // is buffered internally anyhow, so there would be no performance - // boost gained, if anything it would be worse! - int c = l_inStream.read(); - int p = 0; - while (c > -1 && p < l_length) - { - los.write(c); - c = l_inStream.read(); - p++; - } - los.close(); - } - catch (IOException se) - { - throw new PSQLException("postgresql.unusual", se); - } - // lob is closed by the stream so don't call lob.close() - setInt(i, oid); - } - - /* - * This is similar to setBinaryStream except it uses a Reader instead of - * InputStream. - */ - public void setCharacterStream(int i, java.io.Reader x, int length) throws SQLException - { - if (connection.haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports CharacterStream for for the PG text types - //As the spec/javadoc for this method indicate this is to be used for - //large text values (i.e. LONGVARCHAR) PG doesn't have a separate - //long varchar datatype, but with toast all the text datatypes are capable of - //handling very large values. Thus the implementation ends up calling - //setString() since there is no current way to stream the value to the server - char[] l_chars = new char[length]; - int l_charsRead; - try - { - l_charsRead = x.read(l_chars, 0, length); - } - catch (IOException l_ioe) - { - throw new PSQLException("postgresql.unusual", l_ioe); - } - setString(i, new String(l_chars, 0, l_charsRead)); - } - else - { - //Version 7.1 only supported streams for LargeObjects - //but the jdbc spec indicates that streams should be - //available for LONGVARCHAR instead - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - OutputStream los = lob.getOutputStream(); - try - { - // could be buffered, but then the OutputStream returned by LargeObject - // is buffered internally anyhow, so there would be no performance - // boost gained, if anything it would be worse! - int c = x.read(); - int p = 0; - while (c > -1 && p < length) - { - los.write(c); - c = x.read(); - p++; - } - los.close(); - } - catch (IOException se) - { - throw new PSQLException("postgresql.unusual", se); - } - // lob is closed by the stream so don't call lob.close() - setInt(i, oid); - } - } - - /* - * New in 7.1 - */ - public void setClob(int i, Clob x) throws SQLException - { - InputStream l_inStream = x.getAsciiStream(); - int l_length = (int) x.length(); - LargeObjectManager lom = connection.getLargeObjectAPI(); - int oid = lom.create(); - LargeObject lob = lom.open(oid); - OutputStream los = lob.getOutputStream(); - try - { - // could be buffered, but then the OutputStream returned by LargeObject - // is buffered internally anyhow, so there would be no performance - // boost gained, if anything it would be worse! - int c = l_inStream.read(); - int p = 0; - while (c > -1 && p < l_length) - { - los.write(c); - c = l_inStream.read(); - p++; - } - los.close(); - } - catch (IOException se) - { - throw new PSQLException("postgresql.unusual", se); - } - // lob is closed by the stream so don't call lob.close() - setInt(i, oid); - } - - /* - * At least this works as in PostgreSQL null represents anything null ;-) - * - * New in 7,1 - */ - public void setNull(int i, int t, String s) throws SQLException - { - setNull(i, t); - } - - public void setRef(int i, Ref x) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - /* - * New in 7,1 - */ - public void setDate(int i, java.sql.Date d, java.util.Calendar cal) throws SQLException - { - if (cal == null) - setDate(i, d); - else - { - cal.setTime(d); - setDate(i, new java.sql.Date(cal.getTime().getTime())); - } - } - - /* - * New in 7,1 - */ - public void setTime(int i, Time t, java.util.Calendar cal) throws SQLException - { - if (cal == null) - setTime(i, t); - else - { - cal.setTime(t); - setTime(i, new java.sql.Time(cal.getTime().getTime())); - } - } - - /* - * New in 7,1 - */ - public void setTimestamp(int i, Timestamp t, java.util.Calendar cal) throws SQLException - { - if (cal == null) - setTimestamp(i, t); - else - { - cal.setTime(t); - setTimestamp(i, new java.sql.Timestamp(cal.getTime().getTime())); - } - } - -} -