From 68c6eff9454caa57de1b807f58eade19997c9c14 Mon Sep 17 00:00:00 2001 From: Barry Lind Date: Thu, 25 Jul 2002 22:45:28 +0000 Subject: [PATCH] Third phase of restructuring to add jdbc3 support. Modified Files: jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java jdbc/org/postgresql/jdbc1/DatabaseMetaData.java jdbc/org/postgresql/jdbc1/Jdbc1Connection.java jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java jdbc/org/postgresql/jdbc2/Array.java jdbc/org/postgresql/jdbc2/DatabaseMetaData.java jdbc/org/postgresql/jdbc2/Jdbc2Connection.java jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java Added Files: jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java Removed Files: jdbc/org/postgresql/jdbc1/CallableStatement.java jdbc/org/postgresql/jdbc2/CallableStatement.java jdbc/org/postgresql/jdbc2/UpdateableResultSet.java --- .../jdbc1/AbstractJdbc1Connection.java | 5 +- .../jdbc1/AbstractJdbc1ResultSet.java | 13 +- .../jdbc1/AbstractJdbc1Statement.java | 404 +++- .../postgresql/jdbc1/CallableStatement.java | 322 --- .../postgresql/jdbc1/DatabaseMetaData.java | 24 +- .../jdbc1/Jdbc1CallableStatement.java | 14 + .../org/postgresql/jdbc1/Jdbc1Connection.java | 12 +- .../org/postgresql/jdbc1/Jdbc1ResultSet.java | 11 +- .../jdbc2/AbstractJdbc2ResultSet.java | 2065 +++++++++++------ .../jdbc2/AbstractJdbc2Statement.java | 61 +- .../jdbc/org/postgresql/jdbc2/Array.java | 2 +- .../postgresql/jdbc2/CallableStatement.java | 604 ----- .../postgresql/jdbc2/DatabaseMetaData.java | 26 +- .../jdbc2/Jdbc2CallableStatement.java | 15 + .../org/postgresql/jdbc2/Jdbc2Connection.java | 18 +- .../org/postgresql/jdbc2/Jdbc2ResultSet.java | 11 +- .../postgresql/jdbc2/UpdateableResultSet.java | 1389 ----------- 17 files changed, 1873 insertions(+), 3123 deletions(-) delete mode 100644 src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java create mode 100644 src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java delete mode 100644 src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java create mode 100644 src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java delete mode 100644 src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java index 98aa33e5c9..f6d3807bb9 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java @@ -13,7 +13,7 @@ import org.postgresql.largeobject.LargeObjectManager; import org.postgresql.util.*; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.2 2002/07/25 22:45:27 barry Exp $ * This class defines methods of the jdbc1 specification. This class is * extended by org.postgresql.jdbc2.AbstractJdbc2Connection which adds the jdbc2 * methods. The real Connection class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Connection @@ -359,8 +359,7 @@ public abstract class AbstractJdbc1Connection implements org.postgresql.PGConnec // are common to all implementations (JDBC1 or 2), they are placed here. // This should make it easy to maintain the two specifications. -//BJL TODO this method shouldn't need to take a Connection since this can be used. - public abstract java.sql.ResultSet getResultSet(java.sql.Statement stat, org.postgresql.Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException; + public abstract java.sql.ResultSet getResultSet(Statement statement, org.postgresql.Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException; /* * This adds a warning to the warning chain. diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java index 5aa4f90298..3bb278a8e9 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java @@ -13,15 +13,15 @@ import org.postgresql.largeobject.*; import org.postgresql.util.PGbytea; import org.postgresql.util.PSQLException; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1ResultSet.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1ResultSet.java,v 1.2 2002/07/25 22:45:27 barry Exp $ * This class defines methods of the jdbc1 specification. This class is * extended by org.postgresql.jdbc2.AbstractJdbc2ResultSet which adds the jdbc2 * methods. The real ResultSet class (for jdbc1) is org.postgresql.jdbc1.Jdbc1ResultSet */ public abstract class AbstractJdbc1ResultSet { - protected Vector rows; // The results + protected Statement statement; protected Field fields[]; // The field descriptions protected String status; // Status of the result protected boolean binaryCursor = false; // is the data binary or Strings @@ -33,7 +33,7 @@ public abstract class AbstractJdbc1ResultSet protected SQLWarning warnings = null; // The warning chain protected boolean wasNullFlag = false; // the flag for wasNull() - // We can chain multiple resultSets together - this points to + // We can chain multiple resultSets together - this points to // next resultSet in the chain. protected ResultSet next = null; @@ -41,9 +41,10 @@ public abstract class AbstractJdbc1ResultSet public byte[][] rowBuffer=null; - public AbstractJdbc1ResultSet(org.postgresql.PGConnection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) + public AbstractJdbc1ResultSet(org.postgresql.PGConnection conn, Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) { this.connection = conn; + this.statement = statement; this.fields = fields; this.rows = tuples; this.status = status; @@ -116,7 +117,7 @@ public abstract class AbstractJdbc1ResultSet throw new PSQLException("postgresql.res.badbyte", s); } } - return 0; // SQL NULL + return 0; // SQL NULL } public short getShort(int columnIndex) throws SQLException @@ -134,7 +135,7 @@ public abstract class AbstractJdbc1ResultSet throw new PSQLException("postgresql.res.badshort", s); } } - return 0; // SQL NULL + return 0; // SQL NULL } public int getInt(int columnIndex) throws SQLException diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java index 6b06cc873e..96a50efa8f 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java @@ -8,7 +8,7 @@ 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.2 2002/07/24 22:08:39 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.3 2002/07/25 22:45:27 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 @@ -47,6 +47,20 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme protected String[] templateStrings; protected String[] inStrings; + //Used by the callablestatement style methods + private static final String JDBC_SYNTAX = "{[? =] call ([? [,?]*]) }"; + private static final String RESULT_COLUMN = "result"; + private String originalSql = ""; + private boolean isFunction; + // functionReturnType contains the user supplied value to check + // testReturn contains a modified version to make it easier to + // check the getXXX methods.. + private int functionReturnType; + private int testReturn; + // returnTypeSet is true when a proper call to registerOutParameter has been made + private boolean returnTypeSet; + protected Object callResult; + public AbstractJdbc1Statement (AbstractJdbc1Connection connection) @@ -62,6 +76,10 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme } protected void parseSqlStmt () throws SQLException { + if (this instanceof CallableStatement) { + modifyJdbcCall(); + } + Vector v = new Vector(); boolean inQuotes = false; int lastParmEnd = 0, i; @@ -179,7 +197,23 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme // New in 7.1, pass Statement so that ExecSQL can customise to it result = ((AbstractJdbc1Connection)connection).ExecSQL(sql, (java.sql.Statement)this); - return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet()); + //If we are executing a callable statement function set the return data + if (isFunction) { + if (!((AbstractJdbc1ResultSet)result).reallyResultSet()) + throw new PSQLException("postgresql.call.noreturnval"); + if (!result.next ()) + throw new PSQLException ("postgresql.call.noreturnval"); + callResult = result.getObject(1); + int columnType = result.getMetaData().getColumnType(1); + if (columnType != functionReturnType) + throw new PSQLException ("postgresql.call.wrongrtntype", + new Object[]{ + "java.sql.Types=" + columnType, "java.sql.Types="+functionReturnType }); + result.close (); + return true; + } else { + return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet()); + } } /* @@ -233,6 +267,8 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme { if (result == null) return -1; + if (isFunction) + return 1; if (((AbstractJdbc1ResultSet)result).reallyResultSet()) return -1; return ((AbstractJdbc1ResultSet)result).getResultCount(); @@ -253,14 +289,6 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme - - - - - - - - /* * Returns the status message from the current Result.

* This is used internally by the driver. @@ -1214,6 +1242,291 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme setSerialize(parameterIndex, connection.storeObject(x), x.getClass().getName() ); } + /* + * Before executing a stored procedure call you must explicitly + * call registerOutParameter to register the java.sql.Type of each + * out parameter. + * + *

Note: When reading the value of an out parameter, you must use + * the getXXX method whose Java type XXX corresponds to the + * parameter's registered SQL type. + * + * ONLY 1 RETURN PARAMETER if {?= call ..} syntax is used + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param sqlType SQL type code defined by java.sql.Types; for + * parameters of type Numeric or Decimal use the version of + * registerOutParameter that accepts a scale value + * @exception SQLException if a database-access error occurs. + */ + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException + { + if (parameterIndex != 1) + throw new PSQLException ("postgresql.call.noinout"); + if (!isFunction) + throw new PSQLException ("postgresql.call.procasfunc", originalSql); + + // functionReturnType contains the user supplied value to check + // testReturn contains a modified version to make it easier to + // check the getXXX methods.. + functionReturnType = sqlType; + testReturn = sqlType; + if (functionReturnType == Types.CHAR || + functionReturnType == Types.LONGVARCHAR) + testReturn = Types.VARCHAR; + else if (functionReturnType == Types.FLOAT) + testReturn = Types.REAL; // changes to streamline later error checking + returnTypeSet = true; + } + + /* + * You must also specify the scale for numeric/decimal types: + * + *

Note: When reading the value of an out parameter, you must use + * the getXXX method whose Java type XXX corresponds to the + * parameter's registered SQL type. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL + * @param scale a value greater than or equal to zero representing the + * desired number of digits to the right of the decimal point + * @exception SQLException if a database-access error occurs. + */ + public void registerOutParameter(int parameterIndex, int sqlType, + int scale) throws SQLException + { + registerOutParameter (parameterIndex, sqlType); // ignore for now.. + } + + /* + * An OUT parameter may have the value of SQL NULL; wasNull + * reports whether the last value read has this special value. + * + *

Note: You must first call getXXX on a parameter to read its + * value and then call wasNull() to see if the value was SQL NULL. + * @return true if the last parameter read was SQL NULL + * @exception SQLException if a database-access error occurs. + */ + public boolean wasNull() throws SQLException + { + // check to see if the last access threw an exception + return (callResult == null); + } + + /* + * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a + * Java String. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public String getString(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.VARCHAR, "String"); + return (String)callResult; + } + + + /* + * Get the value of a BIT parameter as a Java boolean. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is false + * @exception SQLException if a database-access error occurs. + */ + public boolean getBoolean(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.BIT, "Boolean"); + if (callResult == null) return false; + return ((Boolean)callResult).booleanValue (); + } + + /* + * Get the value of a TINYINT parameter as a Java byte. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public byte getByte(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.TINYINT, "Byte"); + if (callResult == null) return 0; + return (byte)((Integer)callResult).intValue (); + } + + /* + * Get the value of a SMALLINT parameter as a Java short. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public short getShort(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.SMALLINT, "Short"); + if (callResult == null) return 0; + return (short)((Integer)callResult).intValue (); + } + + + /* + * Get the value of an INTEGER parameter as a Java int. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public int getInt(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.INTEGER, "Int"); + if (callResult == null) return 0; + return ((Integer)callResult).intValue (); + } + + /* + * Get the value of a BIGINT parameter as a Java long. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public long getLong(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.BIGINT, "Long"); + if (callResult == null) return 0; + return ((Long)callResult).longValue (); + } + + /* + * Get the value of a FLOAT parameter as a Java float. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public float getFloat(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.REAL, "Float"); + if (callResult == null) return 0; + return ((Float)callResult).floatValue (); + } + + /* + * Get the value of a DOUBLE parameter as a Java double. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is 0 + * @exception SQLException if a database-access error occurs. + */ + public double getDouble(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.DOUBLE, "Double"); + if (callResult == null) return 0; + return ((Double)callResult).doubleValue (); + } + + /* + * Get the value of a NUMERIC parameter as a java.math.BigDecimal + * object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param scale a value greater than or equal to zero representing the + * desired number of digits to the right of the decimal point + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + * @deprecated in Java2.0 + */ + public BigDecimal getBigDecimal(int parameterIndex, int scale) + throws SQLException + { + checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal"); + return ((BigDecimal)callResult); + } + + /* + * Get the value of a SQL BINARY or VARBINARY parameter as a Java + * byte[] + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public byte[] getBytes(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.VARBINARY, "Bytes"); + return ((byte [])callResult); + } + + + /* + * Get the value of a SQL DATE parameter as a java.sql.Date object + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public java.sql.Date getDate(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.DATE, "Date"); + return (java.sql.Date)callResult; + } + + /* + * Get the value of a SQL TIME parameter as a java.sql.Time object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public java.sql.Time getTime(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.TIME, "Time"); + return (java.sql.Time)callResult; + } + + /* + * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return the parameter value; if the value is SQL NULL, the result is null + * @exception SQLException if a database-access error occurs. + */ + public java.sql.Timestamp getTimestamp(int parameterIndex) + throws SQLException + { + checkIndex (parameterIndex, Types.TIMESTAMP, "Timestamp"); + return (java.sql.Timestamp)callResult; + } + + // getObject returns a Java object for the parameter. + // See the JDBC spec's "Dynamic Programming" chapter for details. + /* + * Get the value of a parameter as a Java object. + * + *

This method returns a Java object whose type coresponds to the + * SQL type that was registered for this parameter using + * registerOutParameter. + * + *

Note that this method may be used to read datatabase-specific, + * abstract data types. This is done by specifying a targetSqlType + * of java.sql.types.OTHER, which allows the driver to return a + * database-specific Java type. + * + *

See the JDBC spec's "Dynamic Programming" chapter for details. + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @return A java.lang.Object holding the OUT parameter value. + * @exception SQLException if a database-access error occurs. + */ + public Object getObject(int parameterIndex) + throws SQLException + { + checkIndex (parameterIndex); + return callResult; + } + /* * Returns the SQL statement with the current template values * substituted. @@ -1253,6 +1566,8 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme { if (paramIndex < 1 || paramIndex > inStrings.length) throw new PSQLException("postgresql.prep.range"); + if (paramIndex == 1 && isFunction) // need to registerOut instead + throw new PSQLException ("postgresql.call.funcover"); inStrings[paramIndex - 1] = s; } @@ -1267,6 +1582,13 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme sbuf.setLength(0); int i; + if (isFunction && !returnTypeSet) + throw new PSQLException("postgresql.call.noreturntype"); + if (isFunction) { // set entry 1 to dummy entry.. + inStrings[0] = ""; // dummy entry which ensured that no one overrode + // and calls to setXXX (2,..) really went to first arg in a function call.. + } + for (i = 0 ; i < inStrings.length ; ++i) { if (inStrings[i] == null) @@ -1299,5 +1621,67 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme set(parameterIndex, Long.toString(x) + "::" + tablename ); } + /** + * this method will turn a string of the form + * {? = call (?, [?,..]) } + * into the PostgreSQL format which is + * select (?, [?, ...]) as result + * + */ + private void modifyJdbcCall() throws SQLException { + // syntax checking is not complete only a few basics :( + originalSql = sql; // save for error msgs.. + int index = sql.indexOf ("="); // is implied func or proc? + boolean isValid = true; + if (index != -1) { + isFunction = true; + isValid = sql.indexOf ("?") < index; // ? before = + } + sql = sql.trim (); + if (sql.startsWith ("{") && sql.endsWith ("}")) { + sql = sql.substring (1, sql.length() -1); + } else isValid = false; + index = sql.indexOf ("call"); + if (index == -1 || !isValid) + throw new PSQLException ("postgresql.call.malformed", + new Object[]{sql, JDBC_SYNTAX}); + sql = sql.replace ('{', ' '); // replace these characters + sql = sql.replace ('}', ' '); + sql = sql.replace (';', ' '); + + // this removes the 'call' string and also puts a hidden '?' + // at the front of the line for functions, this will + // allow the registerOutParameter to work correctly + // because in the source sql there was one more ? for the return + // value that is not needed by the postgres syntax. But to make + // sure that the parameter numbers are the same as in the original + // sql we add a dummy parameter in this case + sql = (isFunction ? "?" : "") + sql.substring (index + 4); + + sql = "select " + sql + " as " + RESULT_COLUMN + ";"; + } + + /** helperfunction for the getXXX calls to check isFunction and index == 1 + */ + protected void checkIndex (int parameterIndex, int type, String getName) + throws SQLException { + checkIndex (parameterIndex); + if (type != this.testReturn) + throw new PSQLException("postgresql.call.wrongget", + new Object[]{"java.sql.Types="+testReturn, + getName, + "java.sql.Types="+type}); + } + /** helperfunction for the getXXX calls to check isFunction and index == 1 + * @param parameterIndex index of getXXX (index) + * check to make sure is a function and index == 1 + */ + private void checkIndex (int parameterIndex) throws SQLException { + if (!isFunction) + throw new PSQLException("postgresql.call.noreturntype"); + if (parameterIndex != 1) + throw new PSQLException("postgresql.call.noinout"); + } + } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java deleted file mode 100644 index dab157f97b..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java +++ /dev/null @@ -1,322 +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.sql.*; -import java.math.*; - -/* - * CallableStatement is used to execute SQL stored procedures. - * - *

JDBC provides a stored procedure SQL escape that allows stored - * procedures to be called in a standard way for all RDBMS's. This escape - * syntax has one form that includes a result parameter and one that does - * not. If used, the result parameter must be registered as an OUT - * parameter. The other parameters may be used for input, output or both. - * Parameters are refered to sequentially, by number. The first parameter - * is 1. - * - * {?= call [,, ...]} - * {call [,, ...]} - * - * - *

IN parameter values are set using the set methods inherited from - * PreparedStatement. The type of all OUT parameters must be registered - * prior to executing the stored procedure; their values are retrieved - * after execution via the get methods provided here. - * - *

A Callable statement may return a ResultSet or multiple ResultSets. - * Multiple ResultSets are handled using operations inherited from - * Statement. - * - *

For maximum portability, a call's ResultSets and update counts should - * be processed prior to getting the values of output parameters. - * - * @see Connection#prepareCall - * @see ResultSet - */ - -public class CallableStatement extends Jdbc1PreparedStatement implements java.sql.CallableStatement -{ - /* - * @exception SQLException on failure - */ - CallableStatement(Jdbc1Connection c, String q) throws SQLException - { - super(c, q); - } - - /* - * Before executing a stored procedure call you must explicitly - * call registerOutParameter to register the java.sql.Type of each - * out parameter. - * - *

Note: When reading the value of an out parameter, you must use - * the getXXX method whose Java type XXX corresponds to the - * parameter's registered SQL type. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param sqlType SQL type code defined by java.sql.Types; for - * parameters of type Numeric or Decimal use the version of - * registerOutParameter that accepts a scale value - * @exception SQLException if a database-access error occurs. - */ - public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException - {} - - /* - * You must also specify the scale for numeric/decimal types: - * - *

Note: When reading the value of an out parameter, you must use - * the getXXX method whose Java type XXX corresponds to the - * parameter's registered SQL type. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL - * @param scale a value greater than or equal to zero representing the - * desired number of digits to the right of the decimal point - * @exception SQLException if a database-access error occurs. - */ - public void registerOutParameter(int parameterIndex, int sqlType, - int scale) throws SQLException - {} - - // Old api? - //public boolean isNull(int parameterIndex) throws SQLException { - //return true; - //} - - /* - * An OUT parameter may have the value of SQL NULL; wasNull - * reports whether the last value read has this special value. - * - *

Note: You must first call getXXX on a parameter to read its - * value and then call wasNull() to see if the value was SQL NULL. - * @return true if the last parameter read was SQL NULL - * @exception SQLException if a database-access error occurs. - */ - public boolean wasNull() throws SQLException - { - // check to see if the last access threw an exception - return false; // fake it for now - } - - // Old api? - //public String getChar(int parameterIndex) throws SQLException { - //return null; - //} - - /* - * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a - * Java String. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public String getString(int parameterIndex) throws SQLException - { - return null; - } - //public String getVarChar(int parameterIndex) throws SQLException { - // return null; - //} - - //public String getLongVarChar(int parameterIndex) throws SQLException { - //return null; - //} - - /* - * Get the value of a BIT parameter as a Java boolean. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is false - * @exception SQLException if a database-access error occurs. - */ - public boolean getBoolean(int parameterIndex) throws SQLException - { - return false; - } - - /* - * Get the value of a TINYINT parameter as a Java byte. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public byte getByte(int parameterIndex) throws SQLException - { - return 0; - } - - /* - * Get the value of a SMALLINT parameter as a Java short. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public short getShort(int parameterIndex) throws SQLException - { - return 0; - } - - /* - * Get the value of an INTEGER parameter as a Java int. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public int getInt(int parameterIndex) throws SQLException - { - return 0; - } - - /* - * Get the value of a BIGINT parameter as a Java long. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public long getLong(int parameterIndex) throws SQLException - { - return 0; - } - - /* - * Get the value of a FLOAT parameter as a Java float. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public float getFloat(int parameterIndex) throws SQLException - { - return (float) 0.0; - } - - /* - * Get the value of a DOUBLE parameter as a Java double. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public double getDouble(int parameterIndex) throws SQLException - { - return 0.0; - } - - /* - * Get the value of a NUMERIC parameter as a java.math.BigDecimal - * object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param scale a value greater than or equal to zero representing the - * desired number of digits to the right of the decimal point - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public BigDecimal getBigDecimal(int parameterIndex, int scale) - throws SQLException - { - return null; - } - - /* - * Get the value of a SQL BINARY or VARBINARY parameter as a Java - * byte[] - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public byte[] getBytes(int parameterIndex) throws SQLException - { - return null; - } - - // New API (JPM) (getLongVarBinary) - //public byte[] getBinaryStream(int parameterIndex) throws SQLException { - //return null; - //} - - /* - * Get the value of a SQL DATE parameter as a java.sql.Date object - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Date getDate(int parameterIndex) throws SQLException - { - return null; - } - - /* - * Get the value of a SQL TIME parameter as a java.sql.Time object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Time getTime(int parameterIndex) throws SQLException - { - return null; - } - - /* - * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Timestamp getTimestamp(int parameterIndex) - throws SQLException - { - return null; - } - - //---------------------------------------------------------------------- - // Advanced features: - - // You can obtain a ParameterMetaData object to get information - // about the parameters to this CallableStatement. - //public DatabaseMetaData getMetaData() { - //return null; - //} - - // getObject returns a Java object for the parameter. - // See the JDBC spec's "Dynamic Programming" chapter for details. - /* - * Get the value of a parameter as a Java object. - * - *

This method returns a Java object whose type coresponds to the - * SQL type that was registered for this parameter using - * registerOutParameter. - * - *

Note that this method may be used to read datatabase-specific, - * abstract data types. This is done by specifying a targetSqlType - * of java.sql.types.OTHER, which allows the driver to return a - * database-specific Java type. - * - *

See the JDBC spec's "Dynamic Programming" chapter for details. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return A java.lang.Object holding the OUT parameter value. - * @exception SQLException if a database-access error occurs. - */ - public Object getObject(int parameterIndex) - throws SQLException - { - return null; - } -} - diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java index 747ee1f1ea..bf81f78874 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java @@ -13,7 +13,7 @@ import org.postgresql.util.PSQLException; /* * This class provides information about the database as a whole. * - * $Id: DatabaseMetaData.java,v 1.48 2002/07/23 03:59:55 barry Exp $ + * $Id: DatabaseMetaData.java,v 1.49 2002/07/25 22:45:28 barry Exp $ * *

Many of the methods here return lists of information in ResultSets. You * can use the normal ResultSet methods such as getString and getInt to @@ -1549,7 +1549,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData v.addElement(tuple); } - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -1627,7 +1627,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData // add query loop here - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -1762,7 +1762,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData v.addElement(tuple); } r.close(); - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } // This array contains the valid values for the types argument @@ -1809,7 +1809,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[0] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); tuple[0] = "".getBytes(); v.addElement(tuple); - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -1854,7 +1854,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData tuple[0] = getTableTypes[i][0].getBytes(); v.addElement(tuple); } - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -2050,7 +2050,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData } r.close(); - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -2113,7 +2113,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData //v.addElement(tuple); } - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -2203,7 +2203,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2); f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2); - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -2413,7 +2413,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData while (hasMore); } - return new Jdbc1ResultSet(connection, f, tuples, "OK", 1); + return connection.getResultSet(null, f, tuples, "OK", 1); } /* @@ -2692,7 +2692,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData v.addElement(tuple); } rs.close(); - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } throw new PSQLException("postgresql.metadata.unavailable"); @@ -2832,7 +2832,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData } } - return new Jdbc1ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java new file mode 100644 index 0000000000..7cd69103bd --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1CallableStatement.java @@ -0,0 +1,14 @@ +package org.postgresql.jdbc1; + + +import java.sql.*; + +public class Jdbc1CallableStatement extends AbstractJdbc1Statement implements java.sql.CallableStatement +{ + + public Jdbc1CallableStatement(Jdbc1Connection connection, String sql) throws SQLException + { + super(connection, sql); + } +} + diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1Connection.java index 249a41049b..b67b07fde9 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.2 2002/07/24 22:08:40 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1Connection.java,v 1.3 2002/07/25 22:45:28 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 @@ -24,10 +24,9 @@ public class Jdbc1Connection extends org.postgresql.jdbc1.AbstractJdbc1Connectio return new org.postgresql.jdbc1.Jdbc1PreparedStatement(this, sql); } -//BJL TODO - merge callable statement logic from jdbc2 to jdbc1 public java.sql.CallableStatement prepareCall(String sql) throws SQLException { - throw new PSQLException("postgresql.con.call"); + return new org.postgresql.jdbc1.Jdbc1CallableStatement(this, sql); } public java.sql.DatabaseMetaData getMetaData() throws SQLException @@ -39,7 +38,12 @@ public class Jdbc1Connection extends org.postgresql.jdbc1.AbstractJdbc1Connectio public java.sql.ResultSet getResultSet(java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException { - return new Jdbc1ResultSet(this, fields, tuples, status, updateCount, insertOID, binaryCursor); + return new Jdbc1ResultSet(this, stat, fields, tuples, status, updateCount, insertOID, binaryCursor); + } + + public java.sql.ResultSet getResultSet(java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException + { + return new Jdbc1ResultSet(this, stat, fields, tuples, status, updateCount, 0, false); } } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java index a959fef9d3..57850569ec 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java @@ -5,7 +5,7 @@ import java.sql.*; import java.util.Vector; import org.postgresql.Field; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1ResultSet.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/Jdbc1ResultSet.java,v 1.2 2002/07/25 22:45:28 barry Exp $ * This class implements the java.sql.ResultSet interface for JDBC1. * However most of the implementation is really done in * org.postgresql.jdbc1.AbstractJdbc1ResultSet @@ -13,14 +13,9 @@ import org.postgresql.Field; public class Jdbc1ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet implements java.sql.ResultSet { - public Jdbc1ResultSet(Jdbc1Connection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) + public Jdbc1ResultSet(Jdbc1Connection conn, Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) { - super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor); - } - - public Jdbc1ResultSet(Jdbc1Connection conn, Field[] fields, Vector tuples, String status, int updateCount) - { - super(conn, fields, tuples, status, updateCount, 0, false); + super(conn, statement, fields, tuples, status, updateCount, insertOID, binaryCursor); } public java.sql.ResultSetMetaData getMetaData() throws SQLException diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java index d2c5ee0760..1a6379bf59 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java @@ -6,747 +6,1348 @@ import java.io.*; import java.sql.*; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Vector; +import java.util.*; +import org.postgresql.Driver; import org.postgresql.Field; import org.postgresql.core.Encoding; 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.2 2002/07/24 22:08:42 barry Exp $ + +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2ResultSet.java,v 1.3 2002/07/25 22:45:28 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 Statement statement; - - protected String sqlQuery=null; - - public AbstractJdbc2ResultSet(org.postgresql.PGConnection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) - { - super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor); - } - - public java.net.URL getURL(int columnIndex) throws SQLException - { - return null; - } - - public java.net.URL getURL(String columnName) throws SQLException - { - return null; - } - - /* - * Get the value of a column in the current row as a Java object - * - *

This method will return the value of the given column as a - * Java object. The type of the Java object will be the default - * Java Object type corresponding to the column's SQL type, following - * the mapping specified in the JDBC specification. - * - *

This method may also be used to read database specific abstract - * data types. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Object holding the column value - * @exception SQLException if a database access error occurs - */ - public Object getObject(int columnIndex) throws SQLException - { - Field field; - - checkResultSet( columnIndex ); - - wasNullFlag = (this_row[columnIndex - 1] == null); - if (wasNullFlag) - return null; - - field = fields[columnIndex - 1]; - - // some fields can be null, mainly from those returned by MetaData methods - if (field == null) - { - wasNullFlag = true; - return null; - } - - switch (field.getSQLType()) - { - case Types.BIT: - return getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE; - case Types.SMALLINT: - return new Short(getShort(columnIndex)); - case Types.INTEGER: - return new Integer(getInt(columnIndex)); - case Types.BIGINT: - return new Long(getLong(columnIndex)); - case Types.NUMERIC: - return getBigDecimal - (columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff)); - case Types.REAL: - return new Float(getFloat(columnIndex)); - case Types.DOUBLE: - return new Double(getDouble(columnIndex)); - case Types.CHAR: - case Types.VARCHAR: - return getString(columnIndex); - case Types.DATE: - return getDate(columnIndex); - case Types.TIME: - return getTime(columnIndex); - case Types.TIMESTAMP: - return getTimestamp(columnIndex); - case Types.BINARY: - case Types.VARBINARY: - return getBytes(columnIndex); - case Types.ARRAY: - return getArray(columnIndex); - default: - String type = field.getPGType(); - // if the backend doesn't know the type then coerce to String - if (type.equals("unknown")) - { - return getString(columnIndex); - } - else - { - return connection.getObject(field.getPGType(), getString(columnIndex)); - } - } - } - - public boolean absolute(int index) throws SQLException - { - // index is 1-based, but internally we use 0-based indices - int internalIndex; - - if (index == 0) - throw new SQLException("Cannot move to index of 0"); - - final int rows_size = rows.size(); - - //if index<0, count from the end of the result set, but check - //to be sure that it is not beyond the first index - if (index < 0) - { - if (index >= -rows_size) - internalIndex = rows_size + index; - else - { - beforeFirst(); - return false; - } - } - else - { - //must be the case that index>0, - //find the correct place, assuming that - //the index is not too large - if (index <= rows_size) - internalIndex = index - 1; - else - { - afterLast(); - return false; - } - } - - current_row = internalIndex; - this_row = (byte [][])rows.elementAt(internalIndex); - return true; - } - - public void afterLast() throws SQLException - { - final int rows_size = rows.size(); - if (rows_size > 0) - current_row = rows_size; - } - - public void beforeFirst() throws SQLException - { - if (rows.size() > 0) - current_row = -1; - } - - public void cancelRowUpdates() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void deleteRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public boolean first() throws SQLException - { - if (rows.size() <= 0) - return false; - - current_row = 0; - this_row = (byte [][])rows.elementAt(current_row); - - rowBuffer=new byte[this_row.length][]; - System.arraycopy(this_row,0,rowBuffer,0,this_row.length); - - return true; - } - - public java.sql.Array getArray(String colName) throws SQLException - { - return getArray(findColumn(colName)); - } - - public java.sql.Array getArray(int i) throws SQLException - { - wasNullFlag = (this_row[i - 1] == null); - if (wasNullFlag) - return null; - - if (i < 1 || i > fields.length) - throw new PSQLException("postgresql.res.colrange"); - return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i - 1], (java.sql.ResultSet)this ); - } - - public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException - { - return getBigDecimal(columnIndex, -1); - } - - public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException - { - return getBigDecimal(findColumn(columnName)); - } - - public Blob getBlob(String columnName) throws SQLException - { - return getBlob(findColumn(columnName)); - } - - public Blob getBlob(int i) throws SQLException - { - return new org.postgresql.largeobject.PGblob(connection, getInt(i)); - } - - public java.io.Reader getCharacterStream(String columnName) throws SQLException - { - return getCharacterStream(findColumn(columnName)); - } - - public java.io.Reader getCharacterStream(int i) throws SQLException - { - checkResultSet( i ); - wasNullFlag = (this_row[i - 1] == null); - if (wasNullFlag) - return null; - - if (((AbstractJdbc2Connection)connection).haveMinimumCompatibleVersion("7.2")) - { - //Version 7.2 supports AsciiStream for all 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 string datatype, but with toast the text datatype is capable of - //handling very large values. Thus the implementation ends up calling - //getString() since there is no current way to stream the value from the server - return new CharArrayReader(getString(i).toCharArray()); - } - else - { - // In 7.1 Handle as BLOBS so return the LargeObject input stream - Encoding encoding = connection.getEncoding(); - InputStream input = getBinaryStream(i); - return encoding.getDecodingReader(input); - } - } - - public Clob getClob(String columnName) throws SQLException - { - return getClob(findColumn(columnName)); - } - - public Clob getClob(int i) throws SQLException - { - return new org.postgresql.largeobject.PGclob(connection, getInt(i)); - } - - public int getConcurrency() throws SQLException - { - // The standard ResultSet class will now return - // CONCUR_READ_ONLY. A sub-class will overide this if the query was - // updateable. - return java.sql.ResultSet.CONCUR_READ_ONLY; - } - - public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException - { - // If I read the specs, this should use cal only if we don't - // store the timezone, and if we do, then act just like getDate()? - // for now... - return getDate(i); - } - - public Time getTime(int i, java.util.Calendar cal) throws SQLException - { - // If I read the specs, this should use cal only if we don't - // store the timezone, and if we do, then act just like getTime()? - // for now... - return getTime(i); - } - - public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException - { - // If I read the specs, this should use cal only if we don't - // store the timezone, and if we do, then act just like getDate()? - // for now... - return getTimestamp(i); - } - - public java.sql.Date getDate(String c, java.util.Calendar cal) throws SQLException - { - return getDate(findColumn(c), cal); - } - - public Time getTime(String c, java.util.Calendar cal) throws SQLException - { - return getTime(findColumn(c), cal); - } - - public Timestamp getTimestamp(String c, java.util.Calendar cal) throws SQLException - { - return getTimestamp(findColumn(c), cal); - } - - public int getFetchDirection() throws SQLException - { - //PostgreSQL normally sends rows first->last - return java.sql.ResultSet.FETCH_FORWARD; - } - - public int getFetchSize() throws SQLException - { - // In this implementation we return the entire result set, so - // here return the number of rows we have. Sub-classes can return a proper - // value - return rows.size(); - } - - public Object getObject(String columnName, java.util.Map map) throws SQLException - { - return getObject(findColumn(columnName), map); - } - - /* - * This checks against map for the type of column i, and if found returns - * an object based on that mapping. The class must implement the SQLData - * interface. - */ - public Object getObject(int i, java.util.Map map) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Ref getRef(String columnName) throws SQLException - { - return getRef(findColumn(columnName)); - } - - public Ref getRef(int i) throws SQLException - { - //The backend doesn't yet have SQL3 REF types - throw new PSQLException("postgresql.psqlnotimp"); - } - - public int getRow() throws SQLException - { - final int rows_size = rows.size(); - - if (current_row < 0 || current_row >= rows_size) - return 0; - - return current_row + 1; - } - - // This one needs some thought, as not all ResultSets come from a statement - public Statement getStatement() throws SQLException - { - return statement; - } - - public int getType() throws SQLException - { - // This implementation allows scrolling but is not able to - // see any changes. Sub-classes may overide this to return a more - // meaningful result. - return java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; - } - - public void insertRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public boolean isAfterLast() throws SQLException - { - final int rows_size = rows.size(); - return (current_row >= rows_size && rows_size > 0); - } - - public boolean isBeforeFirst() throws SQLException - { - return (current_row < 0 && rows.size() > 0); - } - - public boolean isFirst() throws SQLException - { - return (current_row == 0 && rows.size() >= 0); - } - - public boolean isLast() throws SQLException - { - final int rows_size = rows.size(); - return (current_row == rows_size - 1 && rows_size > 0); - } - - public boolean last() throws SQLException - { - final int rows_size = rows.size(); - if (rows_size <= 0) - return false; - - current_row = rows_size - 1; - this_row = (byte [][])rows.elementAt(current_row); - - rowBuffer=new byte[this_row.length][]; - System.arraycopy(this_row,0,rowBuffer,0,this_row.length); - - return true; - } - - public void moveToCurrentRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void moveToInsertRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public boolean previous() throws SQLException - { - if (--current_row < 0) - return false; - this_row = (byte [][])rows.elementAt(current_row); - System.arraycopy(this_row,0,rowBuffer,0,this_row.length); - return true; - } - - public void refreshRow() throws SQLException - { - throw new PSQLException("postgresql.notsensitive"); - } - - public boolean relative(int rows) throws SQLException - { - //have to add 1 since absolute expects a 1-based index - return absolute(current_row + 1 + rows); - } - - public boolean rowDeleted() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - return false; // javac complains about not returning a value! - } - - public boolean rowInserted() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - return false; // javac complains about not returning a value! - } - - public boolean rowUpdated() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - return false; // javac complains about not returning a value! - } - - public void setFetchDirection(int direction) throws SQLException - { - throw new PSQLException("postgresql.psqlnotimp"); - } - - public void setFetchSize(int rows) throws SQLException - { - // Sub-classes should implement this as part of their cursor support - throw org.postgresql.Driver.notImplemented(); - } - - public void updateAsciiStream(int columnIndex, - java.io.InputStream x, - int length - ) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateAsciiStream(String columnName, - java.io.InputStream x, - int length - ) throws SQLException - { - updateAsciiStream(findColumn(columnName), x, length); - } - - public void updateBigDecimal(int columnIndex, - java.math.BigDecimal x - ) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateBigDecimal(String columnName, - java.math.BigDecimal x - ) throws SQLException - { - updateBigDecimal(findColumn(columnName), x); - } - - public void updateBinaryStream(int columnIndex, - java.io.InputStream x, - int length - ) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateBinaryStream(String columnName, - java.io.InputStream x, - int length - ) throws SQLException - { - updateBinaryStream(findColumn(columnName), x, length); - } - - public void updateBoolean(int columnIndex, boolean x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateBoolean(String columnName, boolean x) throws SQLException - { - updateBoolean(findColumn(columnName), x); - } - - public void updateByte(int columnIndex, byte x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateByte(String columnName, byte x) throws SQLException - { - updateByte(findColumn(columnName), x); - } - - public void updateBytes(String columnName, byte[] x) throws SQLException - { - updateBytes(findColumn(columnName), x); - } - - public void updateBytes(int columnIndex, byte[] x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateCharacterStream(int columnIndex, - java.io.Reader x, - int length - ) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateCharacterStream(String columnName, - java.io.Reader x, - int length - ) throws SQLException - { - updateCharacterStream(findColumn(columnName), x, length); - } - - public void updateDate(int columnIndex, java.sql.Date x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateDate(String columnName, java.sql.Date x) throws SQLException - { - updateDate(findColumn(columnName), x); - } - - public void updateDouble(int columnIndex, double x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateDouble(String columnName, double x) throws SQLException - { - updateDouble(findColumn(columnName), x); - } - - public void updateFloat(int columnIndex, float x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateFloat(String columnName, float x) throws SQLException - { - updateFloat(findColumn(columnName), x); - } - - public void updateInt(int columnIndex, int x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateInt(String columnName, int x) throws SQLException - { - updateInt(findColumn(columnName), x); - } - - public void updateLong(int columnIndex, long x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateLong(String columnName, long x) throws SQLException - { - updateLong(findColumn(columnName), x); - } - - public void updateNull(int columnIndex) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateNull(String columnName) throws SQLException - { - updateNull(findColumn(columnName)); - } - - public void updateObject(int columnIndex, Object x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateObject(String columnName, Object x) throws SQLException - { - updateObject(findColumn(columnName), x); - } - - public void updateObject(int columnIndex, Object x, int scale) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateObject(String columnName, Object x, int scale) throws SQLException - { - updateObject(findColumn(columnName), x, scale); - } - - public void updateRow() throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateShort(int columnIndex, short x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateShort(String columnName, short x) throws SQLException - { - updateShort(findColumn(columnName), x); - } - - public void updateString(int columnIndex, String x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateString(String columnName, String x) throws SQLException - { - updateString(findColumn(columnName), x); - } - - public void updateTime(int columnIndex, Time x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateTime(String columnName, Time x) throws SQLException - { - updateTime(findColumn(columnName), x); - } - - public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException - { - // only sub-classes implement CONCUR_UPDATEABLE - notUpdateable(); - } - - public void updateTimestamp(String columnName, Timestamp x) throws SQLException - { - updateTimestamp(findColumn(columnName), x); - } - - // helper method. Throws an SQLException when an update is not possible - public void notUpdateable() throws SQLException - { - throw new PSQLException("postgresql.noupdate"); - } - - /* - * It's used currently by getStatement() but may also with the new core - * package. - */ - public void setStatement(Statement statement) - { - this.statement = statement; - } - - public void setSQLQuery(String sqlQuery) { - this.sqlQuery=sqlQuery; - } +public class AbstractJdbc2ResultSet extends org.postgresql.jdbc1.AbstractJdbc1ResultSet { + protected String sqlQuery = null; + + //needed for updateable result set support + protected boolean updateable = false; + protected boolean doingUpdates = false; + protected boolean onInsertRow = false; + protected Hashtable updateValues = new Hashtable(); + private boolean usingOID = false; // are we using the OID for the primary key? + private Vector primaryKeys; // list of primary keys + private int numKeys = 0; + private boolean singleTable = false; + protected String tableName = null; + protected PreparedStatement updateStatement = null; + protected PreparedStatement insertStatement = null; + protected PreparedStatement deleteStatement = null; + private PreparedStatement selectStatement = null; + + + + public AbstractJdbc2ResultSet(org.postgresql.PGConnection conn, Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) { + super (conn, statement, fields, tuples, status, updateCount, insertOID, binaryCursor); + } + + public java.net.URL getURL(int columnIndex) throws SQLException { + return null; + } + + + public java.net.URL getURL(String columnName) throws SQLException { + return null; + } + + + /* + * Get the value of a column in the current row as a Java object + * + *

This method will return the value of the given column as a + * Java object. The type of the Java object will be the default + * Java Object type corresponding to the column's SQL type, following + * the mapping specified in the JDBC specification. + * + *

This method may also be used to read database specific abstract + * data types. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(int columnIndex) throws SQLException { + Field field; + + checkResultSet( columnIndex ); + + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + field = fields[columnIndex - 1]; + + // some fields can be null, mainly from those returned by MetaData methods + if (field == null) { + wasNullFlag = true; + return null; + } + + switch (field.getSQLType()) { + case Types.BIT: + return getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE; + + case Types.SMALLINT: + return new Short(getShort(columnIndex)); + + case Types.INTEGER: + return new Integer(getInt(columnIndex)); + + case Types.BIGINT: + return new Long(getLong(columnIndex)); + + case Types.NUMERIC: + return getBigDecimal + (columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff)); + + case Types.REAL: + return new Float(getFloat(columnIndex)); + + case Types.DOUBLE: + return new Double(getDouble(columnIndex)); + + case Types.CHAR: + case Types.VARCHAR: + return getString(columnIndex); + + case Types.DATE: + return getDate(columnIndex); + + case Types.TIME: + return getTime(columnIndex); + + case Types.TIMESTAMP: + return getTimestamp(columnIndex); + + case Types.BINARY: + case Types.VARBINARY: + return getBytes(columnIndex); + + case Types.ARRAY: + return getArray(columnIndex); + + default: + String type = field.getPGType(); + // if the backend doesn't know the type then coerce to String + if (type.equals("unknown")) { + return getString(columnIndex); + } + else { + return connection.getObject(field.getPGType(), getString(columnIndex)); + } + } + } + + + public boolean absolute(int index) throws SQLException { + // index is 1-based, but internally we use 0-based indices + int internalIndex; + + if (index == 0) + throw new SQLException("Cannot move to index of 0"); + + final int rows_size = rows.size(); + + //if index<0, count from the end of the result set, but check + //to be sure that it is not beyond the first index + if (index < 0) { + if (index >= -rows_size) + internalIndex = rows_size + index; + else { + beforeFirst(); + return false; + } + } + else { + //must be the case that index>0, + //find the correct place, assuming that + //the index is not too large + if (index <= rows_size) + internalIndex = index - 1; + else { + afterLast(); + return false; + } + } + + current_row = internalIndex; + this_row = (byte[][]) rows.elementAt(internalIndex); + return true; + } + + + public void afterLast() throws SQLException { + final int rows_size = rows.size(); + if (rows_size > 0) + current_row = rows_size; + } + + + public void beforeFirst() throws SQLException { + if (rows.size() > 0) + current_row = -1; + } + + + public boolean first() throws SQLException { + if (rows.size() <= 0) + return false; + + current_row = 0; + this_row = (byte[][]) rows.elementAt(current_row); + + rowBuffer = new byte[this_row.length][]; + System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); + + return true; + } + + + public java.sql.Array getArray(String colName) throws SQLException { + return getArray(findColumn(colName)); + } + + + public java.sql.Array getArray(int i) throws SQLException { + wasNullFlag = (this_row[i - 1] == null); + if (wasNullFlag) + return null; + + if (i < 1 || i > fields.length) + throw new PSQLException("postgresql.res.colrange"); + return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i - 1], (java.sql.ResultSet) this ); + } + + + public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException { + return getBigDecimal(columnIndex, -1); + } + + + public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException { + return getBigDecimal(findColumn(columnName)); + } + + + public Blob getBlob(String columnName) throws SQLException { + return getBlob(findColumn(columnName)); + } + + + public Blob getBlob(int i) throws SQLException { + return new org.postgresql.largeobject.PGblob(connection, getInt(i)); + } + + + public java.io.Reader getCharacterStream(String columnName) throws SQLException { + return getCharacterStream(findColumn(columnName)); + } + + + public java.io.Reader getCharacterStream(int i) throws SQLException { + checkResultSet( i ); + wasNullFlag = (this_row[i - 1] == null); + if (wasNullFlag) + return null; + + if (((AbstractJdbc2Connection) connection).haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports AsciiStream for all 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 string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + return new CharArrayReader(getString(i).toCharArray()); + } + else { + // In 7.1 Handle as BLOBS so return the LargeObject input stream + Encoding encoding = connection.getEncoding(); + InputStream input = getBinaryStream(i); + return encoding.getDecodingReader(input); + } + } + + + public Clob getClob(String columnName) throws SQLException { + return getClob(findColumn(columnName)); + } + + + public Clob getClob(int i) throws SQLException { + return new org.postgresql.largeobject.PGclob(connection, getInt(i)); + } + + + public int getConcurrency() throws SQLException { + if (statement == null) + return java.sql.ResultSet.CONCUR_READ_ONLY; + return statement.getResultSetConcurrency(); + } + + + public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException { + // If I read the specs, this should use cal only if we don't + // store the timezone, and if we do, then act just like getDate()? + // for now... + return getDate(i); + } + + + public Time getTime(int i, java.util.Calendar cal) throws SQLException { + // If I read the specs, this should use cal only if we don't + // store the timezone, and if we do, then act just like getTime()? + // for now... + return getTime(i); + } + + + public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException { + // If I read the specs, this should use cal only if we don't + // store the timezone, and if we do, then act just like getDate()? + // for now... + return getTimestamp(i); + } + + + public java.sql.Date getDate(String c, java.util.Calendar cal) throws SQLException { + return getDate(findColumn(c), cal); + } + + + public Time getTime(String c, java.util.Calendar cal) throws SQLException { + return getTime(findColumn(c), cal); + } + + + public Timestamp getTimestamp(String c, java.util.Calendar cal) throws SQLException { + return getTimestamp(findColumn(c), cal); + } + + + public int getFetchDirection() throws SQLException { + //PostgreSQL normally sends rows first->last + return java.sql.ResultSet.FETCH_FORWARD; + } + + + public int getFetchSize() throws SQLException { + // In this implementation we return the entire result set, so + // here return the number of rows we have. Sub-classes can return a proper + // value + return rows.size(); + } + + + public Object getObject(String columnName, java.util.Map map) throws SQLException { + return getObject(findColumn(columnName), map); + } + + + /* + * This checks against map for the type of column i, and if found returns + * an object based on that mapping. The class must implement the SQLData + * interface. + */ + public Object getObject(int i, java.util.Map map) throws SQLException { + throw org.postgresql.Driver.notImplemented(); + } + + + public Ref getRef(String columnName) throws SQLException { + return getRef(findColumn(columnName)); + } + + + public Ref getRef(int i) throws SQLException { + //The backend doesn't yet have SQL3 REF types + throw new PSQLException("postgresql.psqlnotimp"); + } + + + public int getRow() throws SQLException { + final int rows_size = rows.size(); + + if (current_row < 0 || current_row >= rows_size) + return 0; + + return current_row + 1; + } + + + // This one needs some thought, as not all ResultSets come from a statement + public Statement getStatement() throws SQLException { + return statement; + } + + + public int getType() throws SQLException { + // This implementation allows scrolling but is not able to + // see any changes. Sub-classes may overide this to return a more + // meaningful result. + return java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; + } + + + public boolean isAfterLast() throws SQLException { + final int rows_size = rows.size(); + return (current_row >= rows_size && rows_size > 0); + } + + + public boolean isBeforeFirst() throws SQLException { + return (current_row < 0 && rows.size() > 0); + } + + + public boolean isFirst() throws SQLException { + return (current_row == 0 && rows.size() >= 0); + } + + + public boolean isLast() throws SQLException { + final int rows_size = rows.size(); + return (current_row == rows_size - 1 && rows_size > 0); + } + + + public boolean last() throws SQLException { + final int rows_size = rows.size(); + if (rows_size <= 0) + return false; + + current_row = rows_size - 1; + this_row = (byte[][]) rows.elementAt(current_row); + + rowBuffer = new byte[this_row.length][]; + System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); + + return true; + } + + + public boolean previous() throws SQLException { + if (--current_row < 0) + return false; + this_row = (byte[][]) rows.elementAt(current_row); + System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); + return true; + } + + + public boolean relative(int rows) throws SQLException { + //have to add 1 since absolute expects a 1-based index + return absolute(current_row + 1 + rows); + } + + + public void setFetchDirection(int direction) throws SQLException { + throw new PSQLException("postgresql.psqlnotimp"); + } + + + public void setFetchSize(int rows) throws SQLException { + // Sub-classes should implement this as part of their cursor support + throw org.postgresql.Driver.notImplemented(); + } + + + public synchronized void cancelRowUpdates() throws SQLException { + if (doingUpdates) { + doingUpdates = false; + + clearRowBuffer(); + } + } + + + public synchronized void deleteRow() throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if (onInsertRow) { + throw new PSQLException( "postgresql.updateable.oninsertrow" ); + } + + if (rows.size() == 0) { + throw new PSQLException( "postgresql.updateable.emptydelete" ); + } + if (isBeforeFirst()) { + throw new PSQLException( "postgresql.updateable.beforestartdelete" ); + } + if (isAfterLast()) { + throw new PSQLException( "postgresql.updateable.afterlastdelete" ); + } + + + int numKeys = primaryKeys.size(); + if ( deleteStatement == null ) { + + + StringBuffer deleteSQL = new StringBuffer("DELETE FROM " ).append(tableName).append(" where " ); + + for ( int i = 0; i < numKeys; i++ ) { + deleteSQL.append( ((PrimaryKey) primaryKeys.get(i)).name ).append( " = ? " ); + if ( i < numKeys - 1 ) { + deleteSQL.append( " and " ); + } + } + + deleteStatement = ((java.sql.Connection) connection).prepareStatement(deleteSQL.toString()); + } + deleteStatement.clearParameters(); + + for ( int i = 0; i < numKeys; i++ ) { + deleteStatement.setObject(i + 1, ((PrimaryKey) primaryKeys.get(i)).getValue()); + } + + + deleteStatement.executeUpdate(); + + rows.removeElementAt(current_row); + } + + + public synchronized void insertRow() throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if (!onInsertRow) { + throw new PSQLException( "postgresql.updateable.notoninsertrow" ); + } + else { + + // loop through the keys in the insertTable and create the sql statement + // we have to create the sql every time since the user could insert different + // columns each time + + StringBuffer insertSQL = new StringBuffer("INSERT INTO ").append(tableName).append(" ("); + StringBuffer paramSQL = new StringBuffer(") values (" ); + + Enumeration columnNames = updateValues.keys(); + int numColumns = updateValues.size(); + + for ( int i = 0; columnNames.hasMoreElements(); i++ ) { + String columnName = (String) columnNames.nextElement(); + + insertSQL.append( columnName ); + if ( i < numColumns - 1 ) { + insertSQL.append(", "); + paramSQL.append("?,"); + } + else { + paramSQL.append("?)"); + } + + } + + insertSQL.append(paramSQL.toString()); + insertStatement = ((java.sql.Connection) connection).prepareStatement(insertSQL.toString()); + + Enumeration keys = updateValues.keys(); + + for ( int i = 1; keys.hasMoreElements(); i++) { + String key = (String) keys.nextElement(); + insertStatement.setObject(i, updateValues.get( key ) ); + } + + insertStatement.executeUpdate(); + + if ( usingOID ) { + // we have to get the last inserted OID and put it in the resultset + + long insertedOID = ((AbstractJdbc2Statement) insertStatement).getLastOID(); + + updateValues.put("oid", new Long(insertedOID) ); + + } + + // update the underlying row to the new inserted data + updateRowBuffer(); + + rows.addElement(rowBuffer); + + // we should now reflect the current data in this_row + // that way getXXX will get the newly inserted data + this_row = rowBuffer; + + // need to clear this in case of another insert + clearRowBuffer(); + + + } + } + + + public synchronized void moveToCurrentRow() throws SQLException { + if (!updateable) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + this_row = (byte[][]) rows.elementAt(current_row); + + rowBuffer = new byte[this_row.length][]; + System.arraycopy(this_row, 0, rowBuffer, 0, this_row.length); + + onInsertRow = false; + doingUpdates = false; + } + + + public synchronized void moveToInsertRow() throws SQLException { + if (!updateable) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if (insertStatement != null) { + insertStatement = null; + } + + + // make sure the underlying data is null + clearRowBuffer(); + + onInsertRow = true; + doingUpdates = false; + + } + + + private synchronized void clearRowBuffer() throws SQLException { + // rowBuffer is the temporary storage for the row + rowBuffer = new byte[fields.length][]; + + // clear the updateValues hashTable for the next set of updates + updateValues.clear(); + + } + + + public boolean rowDeleted() throws SQLException { + // only sub-classes implement CONCURuPDATEABLE + throw Driver.notImplemented(); + } + + + public boolean rowInserted() throws SQLException { + // only sub-classes implement CONCURuPDATEABLE + throw Driver.notImplemented(); + } + + + public boolean rowUpdated() throws SQLException { + // only sub-classes implement CONCURuPDATEABLE + throw Driver.notImplemented(); + } + + + public synchronized void updateAsciiStream(int columnIndex, + java.io.InputStream x, + int length + ) throws SQLException { + + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + byte[] theData = null; + + try { + x.read(theData, 0, length); + } + catch (NullPointerException ex ) { + throw new PSQLException("postgresql.updateable.inputstream"); + } + catch (IOException ie) { + throw new PSQLException("postgresql.updateable.ioerror" + ie); + } + + doingUpdates = !onInsertRow; + + updateValues.put( fields[columnIndex - 1].getName(), theData ); + + } + + + public synchronized void updateBigDecimal(int columnIndex, + java.math.BigDecimal x ) + throws SQLException { + + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), x ); + + } + + + public synchronized void updateBinaryStream(int columnIndex, + java.io.InputStream x, + int length + ) throws SQLException { + + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + byte[] theData = null; + + try { + x.read(theData, 0, length); + + } + catch ( NullPointerException ex ) { + throw new PSQLException("postgresql.updateable.inputstream"); + } + catch (IOException ie) { + throw new PSQLException("postgresql.updateable.ioerror" + ie); + } + + doingUpdates = !onInsertRow; + + updateValues.put( fields[columnIndex - 1].getName(), theData ); + + } + + + public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException { + + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if ( Driver.logDebug ) Driver.debug("updating boolean " + fields[columnIndex - 1].getName() + "=" + x); + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), new Boolean(x) ); + + } + + + public synchronized void updateByte(int columnIndex, byte x) throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + doingUpdates = true; + updateValues.put( fields[columnIndex - 1].getName(), String.valueOf(x) ); + } + + + public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException { + + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), x ); + + } + + + public synchronized void updateCharacterStream(int columnIndex, + java.io.Reader x, + int length + ) throws SQLException { + + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + char[] theData = null; + + try { + x.read(theData, 0, length); + + } + catch (NullPointerException ex) { + throw new PSQLException("postgresql.updateable.inputstream"); + } + catch (IOException ie) { + throw new PSQLException("postgresql.updateable.ioerror" + ie); + } + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), theData); + + } + + + public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException { + + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), x ); + } + + + public synchronized void updateDouble(int columnIndex, double x) throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if ( Driver.logDebug ) Driver.debug("updating double " + fields[columnIndex - 1].getName() + "=" + x); + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), new Double(x) ); + + } + + + public synchronized void updateFloat(int columnIndex, float x) throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if ( Driver.logDebug ) Driver.debug("updating float " + fields[columnIndex - 1].getName() + "=" + x); + + doingUpdates = !onInsertRow; + + updateValues.put( fields[columnIndex - 1].getName(), new Float(x) ); + + } + + + public synchronized void updateInt(int columnIndex, int x) throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if ( Driver.logDebug ) Driver.debug("updating int " + fields[columnIndex - 1].getName() + "=" + x); + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), new Integer(x) ); + + } + + + public synchronized void updateLong(int columnIndex, long x) throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if ( Driver.logDebug ) Driver.debug("updating long " + fields[columnIndex - 1].getName() + "=" + x); + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), new Long(x) ); + + } + + + public synchronized void updateNull(int columnIndex) throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), null); + + + } + + + public synchronized void updateObject(int columnIndex, Object x) throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if ( Driver.logDebug ) Driver.debug("updating object " + fields[columnIndex - 1].getName() + " = " + x); + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), x ); + } + + + public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + this.updateObject(columnIndex, x); + + } + + + public void refreshRow() throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + try { + StringBuffer selectSQL = new StringBuffer( "select "); + + final int numColumns = java.lang.reflect.Array.getLength(fields); + + for (int i = 0; i < numColumns; i++ ) { + + selectSQL.append( fields[i].getName() ); + + if ( i < numColumns - 1 ) { + + selectSQL.append(", "); + } + + } + selectSQL.append(" from " ).append(tableName).append(" where "); + + int numKeys = primaryKeys.size(); + + for ( int i = 0; i < numKeys; i++ ) { + + PrimaryKey primaryKey = ((PrimaryKey) primaryKeys.get(i)); + selectSQL.append(primaryKey.name).append("= ?"); + + if ( i < numKeys - 1 ) { + selectSQL.append(" and "); + } + } + if ( Driver.logDebug ) Driver.debug("selecting " + selectSQL.toString()); + selectStatement = ((java.sql.Connection) connection).prepareStatement(selectSQL.toString()); + + + for ( int j = 0, i = 1; j < numKeys; j++, i++) { + selectStatement.setObject( i, ((PrimaryKey) primaryKeys.get(j)).getValue() ); + } + + Jdbc2ResultSet rs = (Jdbc2ResultSet) selectStatement.executeQuery(); + + if ( rs.first() ) { + rowBuffer = rs.rowBuffer; + } + + rows.setElementAt( rowBuffer, current_row ); + if ( Driver.logDebug ) Driver.debug("done updates"); + + rs.close(); + selectStatement.close(); + selectStatement = null; + + } + catch (Exception e) { + if ( Driver.logDebug ) Driver.debug(e.getClass().getName() + e); + throw new SQLException( e.getMessage() ); + } + + } + + + public synchronized void updateRow() throws SQLException { + if ( !isUpdateable() ) { + throw new PSQLException( "postgresql.updateable.notupdateable" ); + } + + if (doingUpdates) { + + try { + + StringBuffer updateSQL = new StringBuffer("UPDATE " + tableName + " SET "); + + int numColumns = updateValues.size(); + Enumeration columns = updateValues.keys(); + + for (int i = 0; columns.hasMoreElements(); i++ ) { + + String column = (String) columns.nextElement(); + updateSQL.append( column + "= ?"); + + if ( i < numColumns - 1 ) { + + updateSQL.append(", "); + } + + } + updateSQL.append( " WHERE " ); + + int numKeys = primaryKeys.size(); + + for ( int i = 0; i < numKeys; i++ ) { + + PrimaryKey primaryKey = ((PrimaryKey) primaryKeys.get(i)); + updateSQL.append(primaryKey.name).append("= ?"); + + if ( i < numKeys - 1 ) { + updateSQL.append(" and "); + } + } + if ( Driver.logDebug ) Driver.debug("updating " + updateSQL.toString()); + updateStatement = ((java.sql.Connection) connection).prepareStatement(updateSQL.toString()); + + int i = 0; + Iterator iterator = updateValues.values().iterator(); + for (; iterator.hasNext(); i++) { + updateStatement.setObject( i + 1, iterator.next() ); + + } + for ( int j = 0; j < numKeys; j++, i++) { + updateStatement.setObject( i + 1, ((PrimaryKey) primaryKeys.get(j)).getValue() ); + } + + updateStatement.executeUpdate(); + updateStatement.close(); + + updateStatement = null; + updateRowBuffer(); + + + if ( Driver.logDebug ) Driver.debug("copying data"); + System.arraycopy(rowBuffer, 0, this_row, 0, rowBuffer.length); + + rows.setElementAt( rowBuffer, current_row ); + if ( Driver.logDebug ) Driver.debug("done updates"); + + doingUpdates = false; + } + catch (Exception e) { + if ( Driver.logDebug ) Driver.debug(e.getClass().getName() + e); + throw new SQLException( e.getMessage() ); + } + + } + + } + + + public synchronized void updateShort(int columnIndex, short x) throws SQLException { + if ( Driver.logDebug ) Driver.debug("in update Short " + fields[columnIndex - 1].getName() + " = " + x); + + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), new Short(x) ); + + } + + + public synchronized void updateString(int columnIndex, String x) throws SQLException { + if ( Driver.logDebug ) Driver.debug("in update String " + fields[columnIndex - 1].getName() + " = " + x); + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), x ); + + } + + + public synchronized void updateTime(int columnIndex, Time x) throws SQLException { + if ( Driver.logDebug ) Driver.debug("in update Time " + fields[columnIndex - 1].getName() + " = " + x); + + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), x ); + + } + + + public synchronized void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + if ( Driver.logDebug ) Driver.debug("updating Timestamp " + fields[columnIndex - 1].getName() + " = " + x); + + doingUpdates = !onInsertRow; + updateValues.put( fields[columnIndex - 1].getName(), x ); + + + } + + + public synchronized void updateNull(String columnName) throws SQLException { + updateNull(findColumn(columnName)); + } + + + public synchronized void updateBoolean(String columnName, boolean x) throws SQLException { + updateBoolean(findColumn(columnName), x); + } + + + public synchronized void updateByte(String columnName, byte x) throws SQLException { + updateByte(findColumn(columnName), x); + } + + + public synchronized void updateShort(String columnName, short x) throws SQLException { + updateShort(findColumn(columnName), x); + } + + + public synchronized void updateInt(String columnName, int x) throws SQLException { + updateInt(findColumn(columnName), x); + } + + + public synchronized void updateLong(String columnName, long x) throws SQLException { + updateLong(findColumn(columnName), x); + } + + + public synchronized void updateFloat(String columnName, float x) throws SQLException { + updateFloat(findColumn(columnName), x); + } + + + public synchronized void updateDouble(String columnName, double x) throws SQLException { + updateDouble(findColumn(columnName), x); + } + + + public synchronized void updateBigDecimal(String columnName, BigDecimal x) + throws SQLException { + updateBigDecimal(findColumn(columnName), x); + } + + + public synchronized void updateString(String columnName, String x) throws SQLException { + updateString(findColumn(columnName), x); + } + + + public synchronized void updateBytes(String columnName, byte x[]) throws SQLException { + updateBytes(findColumn(columnName), x); + } + + + public synchronized void updateDate(String columnName, java.sql.Date x) + throws SQLException { + updateDate(findColumn(columnName), x); + } + + + public synchronized void updateTime(String columnName, java.sql.Time x) + throws SQLException { + updateTime(findColumn(columnName), x); + } + + + public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x) + throws SQLException { + updateTimestamp(findColumn(columnName), x); + } + + + public synchronized void updateAsciiStream( + String columnName, + java.io.InputStream x, + int length) + throws SQLException { + updateAsciiStream(findColumn(columnName), x, length); + } + + + public synchronized void updateBinaryStream( + String columnName, + java.io.InputStream x, + int length) + throws SQLException { + updateBinaryStream(findColumn(columnName), x, length); + } + + + public synchronized void updateCharacterStream( + String columnName, + java.io.Reader reader, + int length) + throws SQLException { + updateCharacterStream(findColumn(columnName), reader, length); + } + + + public synchronized void updateObject(String columnName, Object x, int scale) + throws SQLException { + updateObject(findColumn(columnName), x); + } + + + public synchronized void updateObject(String columnName, Object x) throws SQLException { + updateObject(findColumn(columnName), x); + } + + + private int _findColumn( String columnName ) { + int i; + + final int flen = fields.length; + for (i = 0; i < flen; ++i) { + if (fields[i].getName().equalsIgnoreCase(columnName)) { + return (i + 1); + } + } + return -1; + } + + + /** + * Is this ResultSet updateable? + */ + + boolean isUpdateable() throws SQLException { + + if (updateable) return true; + + if ( Driver.logDebug ) Driver.debug("checking if rs is updateable"); + + parseQuery(); + + if ( singleTable == false ) { + if ( Driver.logDebug ) Driver.debug("not a single table"); + return false; + } + + if ( Driver.logDebug ) Driver.debug("getting primary keys"); + + // + // Contains the primary key? + // + + primaryKeys = new Vector(); + + // this is not stricty jdbc spec, but it will make things much faster if used + // the user has to select oid, * from table and then we will just use oid + + + usingOID = false; + int oidIndex = _findColumn( "oid" ); + int i = 0; + + + // if we find the oid then just use it + + if ( oidIndex > 0 ) { + i++; + primaryKeys.add( new PrimaryKey( oidIndex, "oid" ) ); + usingOID = true; + } + else { + // otherwise go and get the primary keys and create a hashtable of keys + java.sql.ResultSet rs = ((java.sql.Connection) connection).getMetaData().getPrimaryKeys("", "", tableName); + + + for (; rs.next(); i++ ) { + String columnName = rs.getString(4); // get the columnName + + int index = findColumn( columnName ); + + if ( index > 0 ) { + primaryKeys.add( new PrimaryKey(index, columnName ) ); // get the primary key information + } + } + + rs.close(); + } + + numKeys = primaryKeys.size(); + + if ( Driver.logDebug ) Driver.debug( "no of keys=" + i ); + + if ( i < 1 ) { + throw new SQLException("No Primary Keys"); + } + + updateable = primaryKeys.size() > 0; + + if ( Driver.logDebug ) Driver.debug( "checking primary key " + updateable ); + + return updateable; + } + + + public void parseQuery() { + StringTokenizer st = new StringTokenizer(sqlQuery, " \r\t"); + boolean tableFound = false, tablesChecked = false; + String name = ""; + + singleTable = true; + + while ( !tableFound && !tablesChecked && st.hasMoreTokens() ) { + name = st.nextToken(); + if ( !tableFound ) { + if (name.toLowerCase().equals("from")) { + tableName = st.nextToken(); + tableFound = true; + } + } + else { + tablesChecked = true; + // if the very next token is , then there are multiple tables + singleTable = !name.equalsIgnoreCase(","); + } + } + } + + + private void updateRowBuffer() throws SQLException { + + Enumeration columns = updateValues.keys(); + + while ( columns.hasMoreElements() ) { + String columnName = (String) columns.nextElement(); + int columnIndex = _findColumn( columnName ) - 1; + + switch ( connection.getSQLType( fields[columnIndex].getPGType() ) ) { + + case Types.DECIMAL: + case Types.BIGINT: + case Types.DOUBLE: + case Types.BIT: + case Types.VARCHAR: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + case Types.SMALLINT: + case Types.FLOAT: + case Types.INTEGER: + case Types.CHAR: + case Types.NUMERIC: + case Types.REAL: + case Types.TINYINT: + + try { + rowBuffer[columnIndex] = String.valueOf( updateValues.get( columnName ) ).getBytes(connection.getEncoding().name() ); + } + catch ( UnsupportedEncodingException ex) { + throw new SQLException("Unsupported Encoding " + connection.getEncoding().name()); + } + + case Types.NULL: + continue; + + default: + rowBuffer[columnIndex] = (byte[]) updateValues.get( columnName ); + } + + } + } + + + public void setStatement(Statement statement) { + this.statement = statement; + } + + + public void setSQLQuery(String sqlQuery) { + this.sqlQuery = sqlQuery; + } + + + private class PrimaryKey { + int index; // where in the result set is this primaryKey + String name; // what is the columnName of this primary Key + + PrimaryKey( int index, String name) { + this.index = index; + this.name = name; + } + Object getValue() throws SQLException { + return getObject(index); + } + }; + + + } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java index 47c2c77983..d8d16985ad 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java @@ -2,12 +2,13 @@ package org.postgresql.jdbc2; import java.io.*; +import java.math.*; 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.2 2002/07/24 22:08:42 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/AbstractJdbc2Statement.java,v 1.3 2002/07/25 22:45:28 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 @@ -46,7 +47,7 @@ public abstract class AbstractJdbc2Statement extends org.postgresql.jdbc1.Abstra { boolean l_return = super.execute(sql); //Now do the jdbc2 specific stuff - //required for ResultSet.getStatement() to work + //required for ResultSet.getStatement() to work and updateable resultsets ((AbstractJdbc2ResultSet)result).setStatement((Statement)this); // Added this so that the Updateable resultset knows the query that gave this @@ -331,4 +332,60 @@ public abstract class AbstractJdbc2Statement extends org.postgresql.jdbc1.Abstra setTimestamp(i, new java.sql.Timestamp(cal.getTime().getTime())); } } + + // ** JDBC 2 Extensions for CallableStatement** + + public java.sql.Array getArray(int i) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public java.math.BigDecimal getBigDecimal(int parameterIndex) throws SQLException + { + checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal"); + return ((BigDecimal)callResult); + } + + public Blob getBlob(int i) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Clob getClob(int i) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Object getObject(int i, java.util.Map map) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Ref getRef(int i) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Time getTime(int i, java.util.Calendar cal) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + // no custom types allowed yet.. + public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException + { + throw org.postgresql.Driver.notImplemented(); + } + + } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java index 2105802d66..99cbf0ea8c 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Array.java @@ -340,7 +340,7 @@ public class Array implements java.sql.Array default: throw org.postgresql.Driver.notImplemented(); } - return new Jdbc2ResultSet((org.postgresql.jdbc2.Jdbc2Connection)conn, fields, rows, "OK", 1 ); + return ((Jdbc2Connection)conn).getResultSet(null, fields, rows, "OK", 1 ); } public String toString() diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java deleted file mode 100644 index 51dd9b2c7f..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java +++ /dev/null @@ -1,604 +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.sql.*; -import java.math.*; -import org.postgresql.util.*; -/* - * CallableStatement is used to execute SQL stored procedures. - * - *

JDBC provides a stored procedure SQL escape that allows stored - * procedures to be called in a standard way for all RDBMS's. This escape - * syntax has one form that includes a result parameter and one that does - * not. If used, the result parameter must be registered as an OUT - * parameter. The other parameters may be used for input, output or both. - * Parameters are refered to sequentially, by number. The first parameter - * is 1. - * - * {?= call [,, ...]} - * {call [,, ...]} - * - * - *

IN parameter values are set using the set methods inherited from - * PreparedStatement. The type of all OUT parameters must be registered - * prior to executing the stored procedure; their values are retrieved - * after execution via the get methods provided here. - * - *

A Callable statement may return a ResultSet or multiple ResultSets. - * Multiple ResultSets are handled using operations inherited from - * Statement. - * - *

For maximum portability, a call's ResultSets and update counts should - * be processed prior to getting the values of output parameters. - * - * @see Connection#prepareCall - * @see ResultSet - * @author Paul Bethe (implementer) - */ - -public class CallableStatement extends org.postgresql.jdbc2.Jdbc2PreparedStatement implements java.sql.CallableStatement -{ - /* - * @exception SQLException on failure - */ - public CallableStatement(Jdbc2Connection c, String q) throws SQLException - { - super(c, q); // don't parse yet.. - } - - - /** - * allows this class to tweak the standard JDBC call !see Usage - * -> and replace with the pgsql function syntax - * ie. select as RESULT; - */ - - protected void parseSqlStmt () throws SQLException { - modifyJdbcCall (); - super.parseSqlStmt (); - } - /** - * this method will turn a string of the form - * {? = call (?, [?,..]) } - * into the PostgreSQL format which is - * select (?, [?, ...]) as result - * - */ - private void modifyJdbcCall () throws SQLException { - // syntax checking is not complete only a few basics :( - originalSql = sql; // save for error msgs.. - int index = sql.indexOf ("="); // is implied func or proc? - boolean isValid = true; - if (index != -1) { - isFunction = true; - isValid = sql.indexOf ("?") < index; // ? before = - } - sql = sql.trim (); - if (sql.startsWith ("{") && sql.endsWith ("}")) { - sql = sql.substring (1, sql.length() -1); - } else isValid = false; - index = sql.indexOf ("call"); - if (index == -1 || !isValid) - throw new PSQLException ("postgresql.call.malformed", - new Object[]{sql, JDBC_SYNTAX}); - sql = sql.replace ('{', ' '); // replace these characters - sql = sql.replace ('}', ' '); - sql = sql.replace (';', ' '); - - // this removes the 'call' string and also puts a hidden '?' - // at the front of the line for functions, this will - // allow the registerOutParameter to work correctly - // because in the source sql there was one more ? for the return - // value that is not needed by the postgres syntax. But to make - // sure that the parameter numbers are the same as in the original - // sql we add a dummy parameter in this case - sql = (isFunction ? "?" : "") + sql.substring (index + 4); - - sql = "select " + sql + " as " + RESULT_COLUMN + ";"; - } - - // internals - static final String JDBC_SYNTAX = "{[? =] call ([? [,?]*]) }"; - static final String RESULT_COLUMN = "result"; - String originalSql = ""; - boolean isFunction; - // functionReturnType contains the user supplied value to check - // testReturn contains a modified version to make it easier to - // check the getXXX methods.. - int functionReturnType; - int testReturn; - // returnTypeSet is true when a proper call to registerOutParameter has been made - boolean returnTypeSet; - Object result; - - /* - * Before executing a stored procedure call you must explicitly - * call registerOutParameter to register the java.sql.Type of each - * out parameter. - * - *

Note: When reading the value of an out parameter, you must use - * the getXXX method whose Java type XXX corresponds to the - * parameter's registered SQL type. - * - * ONLY 1 RETURN PARAMETER if {?= call ..} syntax is used - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param sqlType SQL type code defined by java.sql.Types; for - * parameters of type Numeric or Decimal use the version of - * registerOutParameter that accepts a scale value - * @exception SQLException if a database-access error occurs. - */ - public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException - { - if (parameterIndex != 1) - throw new PSQLException ("postgresql.call.noinout"); - if (!isFunction) - throw new PSQLException ("postgresql.call.procasfunc", originalSql); - - // functionReturnType contains the user supplied value to check - // testReturn contains a modified version to make it easier to - // check the getXXX methods.. - functionReturnType = sqlType; - testReturn = sqlType; - if (functionReturnType == Types.CHAR || - functionReturnType == Types.LONGVARCHAR) - testReturn = Types.VARCHAR; - else if (functionReturnType == Types.FLOAT) - testReturn = Types.REAL; // changes to streamline later error checking - returnTypeSet = true; - } - - /** - * allow calls to execute update - * @return 1 if succesful call otherwise 0 - */ - public int executeUpdate() throws SQLException - { - java.sql.ResultSet rs = super.executeQuery (compileQuery()); - if (isFunction) { - if (!rs.next ()) - throw new PSQLException ("postgresql.call.noreturnval"); - result = rs.getObject(1); - int columnType = rs.getMetaData().getColumnType(1); - if (columnType != functionReturnType) - throw new PSQLException ("postgresql.call.wrongrtntype", - new Object[]{ - getSqlTypeName (columnType), getSqlTypeName (functionReturnType) }); - } - rs.close (); - return 1; - } - - - /** - * allow calls to execute update - * @return true if succesful - */ - public boolean execute() throws SQLException - { - return (executeUpdate() == 1); - } - - /* - * You must also specify the scale for numeric/decimal types: - * - *

Note: When reading the value of an out parameter, you must use - * the getXXX method whose Java type XXX corresponds to the - * parameter's registered SQL type. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param sqlType use either java.sql.Type.NUMERIC or java.sql.Type.DECIMAL - * @param scale a value greater than or equal to zero representing the - * desired number of digits to the right of the decimal point - * @exception SQLException if a database-access error occurs. - */ - public void registerOutParameter(int parameterIndex, int sqlType, - int scale) throws SQLException - { - registerOutParameter (parameterIndex, sqlType); // ignore for now.. - } - - /* - * override this method to check for set @ 1 when declared function.. - * - * @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 && isFunction) // need to registerOut instead - throw new PSQLException ("postgresql.call.funcover"); - super.set (paramIndex, s); // else set as usual.. - } - - /* - * 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 - { - if (isFunction && !returnTypeSet) - throw new PSQLException("postgresql.call.noreturntype"); - if (isFunction) { // set entry 1 to dummy entry.. - inStrings[0] = ""; // dummy entry which ensured that no one overrode - // and calls to setXXX (2,..) really went to first arg in a function call.. - } - return super.compileQuery (); - } - - /* - * An OUT parameter may have the value of SQL NULL; wasNull - * reports whether the last value read has this special value. - * - *

Note: You must first call getXXX on a parameter to read its - * value and then call wasNull() to see if the value was SQL NULL. - * @return true if the last parameter read was SQL NULL - * @exception SQLException if a database-access error occurs. - */ - public boolean wasNull() throws SQLException - { - // check to see if the last access threw an exception - return (result == null); - } - - /* - * Get the value of a CHAR, VARCHAR, or LONGVARCHAR parameter as a - * Java String. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public String getString(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.VARCHAR, "String"); - return (String)result; - } - //public String getVarChar(int parameterIndex) throws SQLException { - // return null; - //} - - //public String getLongVarChar(int parameterIndex) throws SQLException { - //return null; - //} - - /* - * Get the value of a BIT parameter as a Java boolean. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is false - * @exception SQLException if a database-access error occurs. - */ - public boolean getBoolean(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.BIT, "Boolean"); - if (result == null) return false; - return ((Boolean)result).booleanValue (); - } - - /* - * Get the value of a TINYINT parameter as a Java byte. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public byte getByte(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.TINYINT, "Byte"); - if (result == null) return 0; - return (byte)((Integer)result).intValue (); - } - - /* - * Get the value of a SMALLINT parameter as a Java short. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public short getShort(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.SMALLINT, "Short"); - if (result == null) return 0; - return (short)((Integer)result).intValue (); - } - - - /* - * Get the value of an INTEGER parameter as a Java int. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public int getInt(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.INTEGER, "Int"); - if (result == null) return 0; - return ((Integer)result).intValue (); - } - - /* - * Get the value of a BIGINT parameter as a Java long. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public long getLong(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.BIGINT, "Long"); - if (result == null) return 0; - return ((Long)result).longValue (); - } - - /* - * Get the value of a FLOAT parameter as a Java float. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public float getFloat(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.REAL, "Float"); - if (result == null) return 0; - return ((Float)result).floatValue (); - } - - /* - * Get the value of a DOUBLE parameter as a Java double. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is 0 - * @exception SQLException if a database-access error occurs. - */ - public double getDouble(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.DOUBLE, "Double"); - if (result == null) return 0; - return ((Double)result).doubleValue (); - } - - /* - * Get the value of a NUMERIC parameter as a java.math.BigDecimal - * object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @param scale a value greater than or equal to zero representing the - * desired number of digits to the right of the decimal point - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - * @deprecated in Java2.0 - */ - public BigDecimal getBigDecimal(int parameterIndex, int scale) - throws SQLException - { - checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal"); - return ((BigDecimal)result); - } - - /* - * Get the value of a SQL BINARY or VARBINARY parameter as a Java - * byte[] - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public byte[] getBytes(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.VARBINARY, "Bytes"); - return ((byte [])result); - } - - // New API (JPM) (getLongVarBinary) - //public byte[] getBinaryStream(int parameterIndex) throws SQLException { - //return null; - //} - - /* - * Get the value of a SQL DATE parameter as a java.sql.Date object - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Date getDate(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.DATE, "Date"); - return (java.sql.Date)result; - } - - /* - * Get the value of a SQL TIME parameter as a java.sql.Time object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Time getTime(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.TIME, "Time"); - return (java.sql.Time)result; - } - - /* - * Get the value of a SQL TIMESTAMP parameter as a java.sql.Timestamp object. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return the parameter value; if the value is SQL NULL, the result is null - * @exception SQLException if a database-access error occurs. - */ - public java.sql.Timestamp getTimestamp(int parameterIndex) - throws SQLException - { - checkIndex (parameterIndex, Types.TIMESTAMP, "Timestamp"); - return (java.sql.Timestamp)result; - } - - //---------------------------------------------------------------------- - // Advanced features: - - // You can obtain a ParameterMetaData object to get information - // about the parameters to this CallableStatement. - //public DatabaseMetaData getMetaData() { - //return null; - //} - - // getObject returns a Java object for the parameter. - // See the JDBC spec's "Dynamic Programming" chapter for details. - /* - * Get the value of a parameter as a Java object. - * - *

This method returns a Java object whose type coresponds to the - * SQL type that was registered for this parameter using - * registerOutParameter. - * - *

Note that this method may be used to read datatabase-specific, - * abstract data types. This is done by specifying a targetSqlType - * of java.sql.types.OTHER, which allows the driver to return a - * database-specific Java type. - * - *

See the JDBC spec's "Dynamic Programming" chapter for details. - * - * @param parameterIndex the first parameter is 1, the second is 2,... - * @return A java.lang.Object holding the OUT parameter value. - * @exception SQLException if a database-access error occurs. - */ - public Object getObject(int parameterIndex) - throws SQLException - { - checkIndex (parameterIndex); - return result; - } - - // ** JDBC 2 Extensions ** - - public java.sql.Array getArray(int i) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public java.math.BigDecimal getBigDecimal(int parameterIndex) throws SQLException - { - checkIndex (parameterIndex, Types.NUMERIC, "BigDecimal"); - return ((BigDecimal)result); - } - - public Blob getBlob(int i) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Clob getClob(int i) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Object getObject(int i, java.util.Map map) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Ref getRef(int i) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Time getTime(int i, java.util.Calendar cal) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - // no custom types allowed yet.. - public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException - { - throw org.postgresql.Driver.notImplemented(); - } - - - - /** helperfunction for the getXXX calls to check isFunction and index == 1 - */ - private void checkIndex (int parameterIndex, int type, String getName) - throws SQLException { - checkIndex (parameterIndex); - if (type != this.testReturn) - throw new PSQLException("postgresql.call.wrongget", - new Object[]{getSqlTypeName (testReturn), - getName, - getSqlTypeName (type)}); - } - /** helperfunction for the getXXX calls to check isFunction and index == 1 - * @param parameterIndex index of getXXX (index) - * check to make sure is a function and index == 1 - */ - private void checkIndex (int parameterIndex) throws SQLException { - if (!isFunction) - throw new PSQLException("postgresql.call.noreturntype"); - if (parameterIndex != 1) - throw new PSQLException("postgresql.call.noinout"); - } - - /** helper function for creating msg with type names - * @param sqlType a java.sql.Types.XX constant - * @return String which is the name of the constant.. - */ - private static String getSqlTypeName (int sqlType) { - switch (sqlType) - { - case Types.BIT: - return "BIT"; - case Types.SMALLINT: - return "SMALLINT"; - case Types.INTEGER: - return "INTEGER"; - case Types.BIGINT: - return "BIGINT"; - case Types.NUMERIC: - return "NUMERIC"; - case Types.REAL: - return "REAL"; - case Types.DOUBLE: - return "DOUBLE"; - case Types.FLOAT: - return "FLOAT"; - case Types.CHAR: - return "CHAR"; - case Types.VARCHAR: - return "VARCHAR"; - case Types.DATE: - return "DATE"; - case Types.TIME: - return "TIME"; - case Types.TIMESTAMP: - return "TIMESTAMP"; - case Types.BINARY: - return "BINARY"; - case Types.VARBINARY: - return "VARBINARY"; - default: - return "UNKNOWN"; - } - } -} - diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java index 77415d051d..a1c8e22737 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java @@ -15,7 +15,7 @@ import org.postgresql.util.PSQLException; /* * This class provides information about the database as a whole. * - * $Id: DatabaseMetaData.java,v 1.59 2002/07/23 03:59:55 barry Exp $ + * $Id: DatabaseMetaData.java,v 1.60 2002/07/25 22:45:28 barry Exp $ * *

Many of the methods here return lists of information in ResultSets. You * can use the normal ResultSet methods such as getString and getInt to @@ -1653,7 +1653,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData v.addElement(tuple); } - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -1731,7 +1731,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData // add query loop here - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -1866,7 +1866,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData v.addElement(tuple); } r.close(); - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } // This array contains the valid values for the types argument @@ -1913,7 +1913,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[0] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); tuple[0] = "".getBytes(); v.addElement(tuple); - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -1958,7 +1958,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData tuple[0] = getTableTypes[i][0].getBytes(); v.addElement(tuple); } - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -2154,7 +2154,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData } r.close(); - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -2218,7 +2218,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData //v.addElement(tuple); } - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -2281,7 +2281,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData //v.addElement(tuple); } - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -2337,7 +2337,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[6] = new Field(connection, "DECIMAL_DIGITS", iInt2Oid, 2); f[7] = new Field(connection, "PSEUDO_COLUMN", iInt2Oid, 2); - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } /* @@ -2680,7 +2680,7 @@ WHERE tuples.addElement(tuple); } - return new Jdbc2ResultSet(connection, f, tuples, "OK", 1); + return connection.getResultSet(null, f, tuples, "OK", 1); } /* @@ -2959,7 +2959,7 @@ WHERE v.addElement(tuple); } rs.close(); - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } throw new PSQLException("postgresql.metadata.unavailable"); @@ -3097,7 +3097,7 @@ WHERE } } - return new Jdbc2ResultSet(connection, f, v, "OK", 1); + return connection.getResultSet(null, f, v, "OK", 1); } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java new file mode 100644 index 0000000000..c8bc1b7907 --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2CallableStatement.java @@ -0,0 +1,15 @@ +package org.postgresql.jdbc2; + + +import java.sql.*; + +public class Jdbc2CallableStatement extends AbstractJdbc2Statement implements java.sql.CallableStatement +{ + + public Jdbc2CallableStatement(Jdbc2Connection connection, String sql) throws SQLException + { + super(connection, sql); + } + +} + diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java index 787b14e62a..b512355260 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2Connection.java @@ -3,9 +3,10 @@ package org.postgresql.jdbc2; import java.sql.*; import java.util.Vector; +import java.util.Hashtable; import org.postgresql.Field; -/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2Connection.java,v 1.2 2002/07/24 22:08:42 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2Connection.java,v 1.3 2002/07/25 22:45:28 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 @@ -32,7 +33,7 @@ public class Jdbc2Connection extends org.postgresql.jdbc2.AbstractJdbc2Connectio public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - org.postgresql.jdbc2.CallableStatement s = new org.postgresql.jdbc2.CallableStatement(this,sql); + Jdbc2CallableStatement s = new org.postgresql.jdbc2.Jdbc2CallableStatement(this,sql); s.setResultSetType(resultSetType); s.setResultSetConcurrency(resultSetConcurrency); return s; @@ -45,15 +46,14 @@ public class Jdbc2Connection extends org.postgresql.jdbc2.AbstractJdbc2Connectio return metadata; } - public java.sql.ResultSet getResultSet(java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException + public java.sql.ResultSet getResultSet(Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException { - if (stat != null) - { - if (stat.getResultSetConcurrency() == java.sql.ResultSet.CONCUR_UPDATABLE) - return new org.postgresql.jdbc2.UpdateableResultSet(this, fields, tuples, status, updateCount, insertOID, binaryCursor); - } + return new Jdbc2ResultSet(this, statement, fields, tuples, status, updateCount, insertOID, binaryCursor); + } - return new Jdbc2ResultSet(this, fields, tuples, status, updateCount, insertOID, binaryCursor); + public java.sql.ResultSet getResultSet(Statement statement, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException + { + return new Jdbc2ResultSet(this, statement, fields, tuples, status, updateCount, 0, false); } diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java index 7200cf549a..e2c6ad7eea 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.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/Jdbc2ResultSet.java,v 1.1 2002/07/23 03:59:55 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Attic/Jdbc2ResultSet.java,v 1.2 2002/07/25 22:45:28 barry Exp $ * This class implements the java.sql.ResultSet interface for JDBC2. * However most of the implementation is really done in * org.postgresql.jdbc2.AbstractJdbc2ResultSet or one of it's parents @@ -13,14 +13,9 @@ import org.postgresql.Field; public class Jdbc2ResultSet extends org.postgresql.jdbc2.AbstractJdbc2ResultSet implements java.sql.ResultSet { - public Jdbc2ResultSet(Jdbc2Connection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) + public Jdbc2ResultSet(Jdbc2Connection conn, Statement statement, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) { - super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor); - } - - public Jdbc2ResultSet(Jdbc2Connection conn, Field[] fields, Vector tuples, String status, int updateCount) - { - super(conn, fields, tuples, status, updateCount, 0, false); + super(conn, statement, fields, tuples, status, updateCount, insertOID, binaryCursor); } public java.sql.ResultSetMetaData getMetaData() throws SQLException diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java b/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java deleted file mode 100644 index 6f6f67d839..0000000000 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java +++ /dev/null @@ -1,1389 +0,0 @@ -package org.postgresql.jdbc2; - -// IMPORTANT NOTE: This is the begining of supporting updateable ResultSets. -// -// This is because here we should be updateable, so any unimplemented methods -// must say so. -// -// Also you'll notice that the String columnName based calls are not present. -// They are not required as they are in the super class. -// - -import java.lang.*; -import java.io.*; -import java.math.*; -import java.text.*; -import java.util.*; -import java.sql.*; -import org.postgresql.Field; -import org.postgresql.largeobject.*; -import org.postgresql.util.*; -import org.postgresql.Driver; - -/* - * @see ResultSet - * @see ResultSetMetaData - * @see java.sql.ResultSet - */ -public class UpdateableResultSet extends org.postgresql.jdbc2.Jdbc2ResultSet -{ - - - class PrimaryKey - { - int index; // where in the result set is this primaryKey - String name; // what is the columnName of this primary Key - - PrimaryKey( int index, String name) - { - this.index = index; - this.name = name; - } - Object getValue() throws SQLException - { - return getObject(index); - } - }; - - private boolean usingOID = false; // are we using the OID for the primary key? - - private Vector primaryKeys; // list of primary keys - - private int numKeys = 0; - - private boolean singleTable = false; - - protected String tableName = null; - - /** - * PreparedStatement used to delete data - */ - - protected java.sql.PreparedStatement updateStatement = null; - - /** - * PreparedStatement used to insert data - */ - - protected java.sql.PreparedStatement insertStatement = null; - - /** - * PreparedStatement used to delete data - */ - - protected java.sql.PreparedStatement deleteStatement = null; - - - /** - * PreparedStatement used to refresh data - */ - private java.sql.PreparedStatement selectStatement = null; - - - /** - * Is this result set updateable? - */ - - protected boolean updateable = false; - - /** - * Are we in the middle of doing updates to the current row? - */ - - protected boolean doingUpdates = false; - - - /** - * Are we on the insert row? - */ - - protected boolean onInsertRow = false; - - - protected Hashtable updateValues = new Hashtable(); - - // The Row Buffer will be used to cache updated rows..then we shall sync this with the rows vector - - - /* - * Create a new ResultSet - Note that we create ResultSets to - * represent the results of everything. - * - * @param fields an array of Field objects (basically, the - * ResultSet MetaData) - * @param tuples Vector of the actual data - * @param status the status string returned from the back end - * @param updateCount the number of rows affected by the operation - * @param cursor the positioned update/delete cursor name - */ - public UpdateableResultSet(Jdbc2Connection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) - { - super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor); - } - - /** - * - * @throws SQLException - */ - public synchronized void cancelRowUpdates() throws SQLException - { - if (doingUpdates) - { - doingUpdates = false; - - clearRowBuffer(); - } - } - - /** - * - * @throws SQLException - */ - public synchronized void deleteRow() throws SQLException - { - if ( !isUpdateable() ) - { - throw new PSQLException( "postgresql.updateable.notupdateable" ); - } - - if (onInsertRow) - { - throw new PSQLException( "postgresql.updateable.oninsertrow" ); - } - - if (rows.size() == 0) - { - throw new PSQLException( "postgresql.updateable.emptydelete" ); - } - if (isBeforeFirst()) - { - throw new PSQLException( "postgresql.updateable.beforestartdelete" ); - } - if (isAfterLast()) - { - throw new PSQLException( "postgresql.updateable.afterlastdelete" ); - } - - - int numKeys = primaryKeys.size(); - if ( deleteStatement == null ) - { - - - StringBuffer deleteSQL= new StringBuffer("DELETE FROM " ).append(tableName).append(" where " ); - - for ( int i=0; i < numKeys ; i++ ) - { - deleteSQL.append( ((PrimaryKey)primaryKeys.get(i)).name ).append( " = ? " ); - if ( i < numKeys-1 ) - { - deleteSQL.append( " and " ); - } - } - - deleteStatement = ((java.sql.Connection)connection).prepareStatement(deleteSQL.toString()); - } - deleteStatement.clearParameters(); - - for ( int i =0; i < numKeys; i++ ) - { - deleteStatement.setObject(i+1, ((PrimaryKey)primaryKeys.get(i)).getValue()); - } - - - deleteStatement.executeUpdate(); - - rows.removeElementAt(current_row); - } - - - /** - * - * @return - * @throws SQLException - */ - public int getConcurrency() throws SQLException - { - // New in 7.1 - The updateable ResultSet class will now return - // CONCURuPDATEABLE. - return CONCUR_UPDATABLE; - } - - /** - * - * @throws SQLException - */ - - public synchronized void insertRow() throws SQLException - { - if ( !isUpdateable() ) - { - throw new PSQLException( "postgresql.updateable.notupdateable" ); - } - - if (!onInsertRow) - { - throw new PSQLException( "postgresql.updateable.notoninsertrow" ); - } - else - { - - // loop through the keys in the insertTable and create the sql statement - // we have to create the sql every time since the user could insert different - // columns each time - - StringBuffer insertSQL=new StringBuffer("INSERT INTO ").append(tableName).append(" ("); - StringBuffer paramSQL = new StringBuffer(") values (" ); - - Enumeration columnNames = updateValues.keys(); - int numColumns = updateValues.size(); - - for ( int i=0; columnNames.hasMoreElements() ; i++ ) - { - String columnName = (String)columnNames.nextElement(); - - insertSQL.append( columnName ); - if ( i < numColumns - 1 ) - { - insertSQL.append(", "); - paramSQL.append("?,"); - } - else - { - paramSQL.append("?)"); - } - - } - - insertSQL.append(paramSQL.toString()); - insertStatement = ((java.sql.Connection)connection).prepareStatement(insertSQL.toString()); - - Enumeration keys = updateValues.keys(); - - for( int i=1; keys.hasMoreElements() ; i++) - { - String key = (String)keys.nextElement(); - insertStatement.setObject(i, updateValues.get( key ) ); - } - - insertStatement.executeUpdate(); - - if ( usingOID ) - { - // we have to get the last inserted OID and put it in the resultset - - long insertedOID = ((AbstractJdbc2Statement)insertStatement).getLastOID(); - - updateValues.put("oid", new Long(insertedOID) ); - - } - - // update the underlying row to the new inserted data - updateRowBuffer(); - - rows.addElement(rowBuffer); - - // we should now reflect the current data in this_row - // that way getXXX will get the newly inserted data - this_row = rowBuffer; - - // need to clear this in case of another insert - clearRowBuffer(); - - - } - } - - - /** - * - * @throws SQLException - */ - - public synchronized void moveToCurrentRow() throws SQLException - { - this_row = (byte [][])rows.elementAt(current_row); - - rowBuffer=new byte[this_row.length][]; - System.arraycopy(this_row,0,rowBuffer,0,this_row.length); - - onInsertRow = false; - doingUpdates = false; - } - - /** - * - * @throws SQLException - */ - public synchronized void moveToInsertRow() throws SQLException - { - // only sub-classes implement CONCURuPDATEABLE - if (!updateable) - { - throw new PSQLException( "postgresql.updateable.notupdateable" ); - } - - if (insertStatement != null) - { - insertStatement = null; - } - - - // make sure the underlying data is null - clearRowBuffer(); - - onInsertRow = true; - doingUpdates = false; - - } - - /** - * - * @throws SQLException - */ - private synchronized void clearRowBuffer() throws SQLException - { - // rowBuffer is the temporary storage for the row - rowBuffer=new byte[fields.length][]; - - // clear the updateValues hashTable for the next set of updates - updateValues.clear(); - - } - - - /** - * - * @return - * @throws SQLException - */ - public boolean rowDeleted() throws SQLException - { - // only sub-classes implement CONCURuPDATEABLE - throw Driver.notImplemented(); - } - - /** - * - * @return - * @throws SQLException - */ - public boolean rowInserted() throws SQLException - { - // only sub-classes implement CONCURuPDATEABLE - throw Driver.notImplemented(); - } - - /** - * - * @return - * @throws SQLException - */ - public boolean rowUpdated() throws SQLException - { - // only sub-classes implement CONCURuPDATEABLE - throw Driver.notImplemented(); - } - - /** - * - * @param columnIndex - * @param x - * @param length - * @throws SQLException - */ - public synchronized void updateAsciiStream(int columnIndex, - java.io.InputStream x, - int length - ) throws SQLException - { - - byte[] theData=null; - - try - { - x.read(theData,0,length); - } - catch (NullPointerException ex ) - { - throw new PSQLException("postgresql.updateable.inputstream"); - } - catch (IOException ie) - { - throw new PSQLException("postgresql.updateable.ioerror" + ie); - } - - doingUpdates = !onInsertRow; - - updateValues.put( fields[columnIndex-1].getName(), theData ); - - } - - /** - * - * @param columnIndex - * @param x - * @throws SQLException - */ - public synchronized void updateBigDecimal(int columnIndex, - java.math.BigDecimal x ) - throws SQLException - { - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), x ); - - } - - /** - * - * @param columnIndex - * @param x - * @param length - * @throws SQLException - */ - public synchronized void updateBinaryStream(int columnIndex, - java.io.InputStream x, - int length - ) throws SQLException - { - - - byte[] theData=null; - - try { - x.read(theData,0,length); - - } - catch( NullPointerException ex ) - { - throw new PSQLException("postgresql.updateable.inputstream"); - } - catch (IOException ie) - { - throw new PSQLException("postgresql.updateable.ioerror" + ie); - } - - doingUpdates = !onInsertRow; - - updateValues.put( fields[columnIndex-1].getName(), theData ); - - } - - /** - * - * @param columnIndex - * @param x - * @throws SQLException - */ - public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException - { - - if ( Driver.logDebug ) Driver.debug("updating boolean "+fields[columnIndex-1].getName()+"="+x); - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), new Boolean(x) ); - - } - - /** - * - * @param columnIndex - * @param x - * @throws SQLException - */ - public synchronized void updateByte(int columnIndex, byte x) throws SQLException - { - - doingUpdates = true; - updateValues.put( fields[columnIndex-1].getName(), String.valueOf(x) ); - } - - /** - * - * @param columnIndex - * @param x - * @throws SQLException - */ - public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException - { - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), x ); - - } - - /** - * - * @param columnIndex - * @param x - * @param length - * @throws SQLException - */ - public synchronized void updateCharacterStream(int columnIndex, - java.io.Reader x, - int length - ) throws SQLException - { - - - char[] theData=null; - - try - { - x.read(theData,0,length); - - } - catch (NullPointerException ex) - { - throw new PSQLException("postgresql.updateable.inputstream"); - } - catch (IOException ie) - { - throw new PSQLException("postgresql.updateable.ioerror" + ie); - } - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), theData); - - } - - public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException - { - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), x ); - } - - public synchronized void updateDouble(int columnIndex, double x) throws SQLException - { - if ( Driver.logDebug ) Driver.debug("updating double "+fields[columnIndex-1].getName()+"="+x); - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), new Double(x) ); - - } - - public synchronized void updateFloat(int columnIndex, float x) throws SQLException - { - if ( Driver.logDebug ) Driver.debug("updating float "+fields[columnIndex-1].getName()+"="+x); - - doingUpdates = !onInsertRow; - - updateValues.put( fields[columnIndex-1].getName(), new Float(x) ); - - } - - public synchronized void updateInt(int columnIndex, int x) throws SQLException - { - if ( Driver.logDebug ) Driver.debug("updating int "+fields[columnIndex-1].getName()+"="+x); - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), new Integer(x) ); - - } - - public synchronized void updateLong(int columnIndex, long x) throws SQLException - { - if ( Driver.logDebug ) Driver.debug("updating long "+fields[columnIndex-1].getName()+"="+x); - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), new Long(x) ); - - } - - public synchronized void updateNull(int columnIndex) throws SQLException - { - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), null); - - - } - - public synchronized void updateObject(int columnIndex, Object x) throws SQLException - { - - - if ( Driver.logDebug ) Driver.debug("updating object " + fields[columnIndex-1].getName() + " = " + x); - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), x ); - } - - public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException - { - - this.updateObject(columnIndex, x); - - } - - - public void refreshRow() throws SQLException - { - if ( !isUpdateable() ) - { - throw new PSQLException( "postgresql.updateable.notupdateable" ); - } - - try - { - StringBuffer selectSQL = new StringBuffer( "select "); - - final int numColumns = java.lang.reflect.Array.getLength(fields); - - for (int i=0; i < numColumns ; i++ ) - { - - selectSQL.append( fields[i].getName() ); - - if ( i < numColumns - 1 ) - { - - selectSQL.append(", "); - } - - } - selectSQL.append(" from " ).append(tableName).append(" where "); - - int numKeys = primaryKeys.size(); - - for ( int i = 0; i < numKeys; i++ ) - { - - PrimaryKey primaryKey = ((PrimaryKey)primaryKeys.get(i)); - selectSQL.append(primaryKey.name).append("= ?"); - - if ( i < numKeys -1 ) - { - selectSQL.append(" and "); - } - } - if ( Driver.logDebug ) Driver.debug("selecting "+ selectSQL.toString()); - selectStatement = ((java.sql.Connection)connection).prepareStatement(selectSQL.toString()); - - - for( int j=0, i=1; j < numKeys; j++, i++) - { - selectStatement.setObject( i, ((PrimaryKey)primaryKeys.get(j)).getValue() ); - } - - Jdbc2ResultSet rs = (Jdbc2ResultSet) selectStatement.executeQuery(); - - if( rs.first() ) - { - rowBuffer = rs.rowBuffer; - } - - rows.setElementAt( rowBuffer, current_row ); - if ( Driver.logDebug ) Driver.debug("done updates"); - - rs.close(); - selectStatement.close(); - selectStatement = null; - - } - catch (Exception e) - { - if ( Driver.logDebug ) Driver.debug(e.getClass().getName()+e); - throw new SQLException( e.getMessage() ); - } - - } - /** - * - * @throws SQLException - */ - public synchronized void updateRow() throws SQLException - { - if ( !isUpdateable() ) - { - throw new PSQLException( "postgresql.updateable.notupdateable" ); - } - - if (doingUpdates) - { - - try - { - - StringBuffer updateSQL=new StringBuffer("UPDATE "+tableName+" SET "); - - int numColumns = updateValues.size(); - Enumeration columns = updateValues.keys(); - - for (int i=0; columns.hasMoreElements() ; i++ ) - { - - String column = (String)columns.nextElement(); - updateSQL.append( column + "= ?"); - - if ( i < numColumns - 1 ) - { - - updateSQL.append(", "); - } - - } - updateSQL.append( " WHERE " ); - - int numKeys = primaryKeys.size(); - - for ( int i = 0; i < numKeys; i++ ) - { - - PrimaryKey primaryKey = ((PrimaryKey)primaryKeys.get(i)); - updateSQL.append(primaryKey.name).append("= ?"); - - if ( i < numKeys -1 ) - { - updateSQL.append(" and "); - } - } - if ( Driver.logDebug ) Driver.debug("updating "+updateSQL.toString()); - updateStatement = ((java.sql.Connection)connection).prepareStatement(updateSQL.toString()); - - int i = 0; - Iterator iterator = updateValues.values().iterator(); - for (; iterator.hasNext(); i++) - { - updateStatement.setObject( i+1, iterator.next() ); - - } - for( int j=0; j < numKeys; j++, i++) - { - updateStatement.setObject( i+1, ((PrimaryKey)primaryKeys.get(j)).getValue() ); - } - - updateStatement.executeUpdate(); - updateStatement.close(); - - updateStatement = null; - updateRowBuffer(); - - - if ( Driver.logDebug ) Driver.debug("copying data"); - System.arraycopy(rowBuffer,0,this_row,0,rowBuffer.length); - - rows.setElementAt( rowBuffer, current_row ); - if ( Driver.logDebug ) Driver.debug("done updates"); - - doingUpdates = false; - } - catch(Exception e) - { - if ( Driver.logDebug ) Driver.debug(e.getClass().getName()+e); - throw new SQLException( e.getMessage() ); - } - - } - - } - - public synchronized void updateShort(int columnIndex, short x) throws SQLException - { - if ( Driver.logDebug ) Driver.debug("in update Short "+fields[columnIndex-1].getName()+" = "+x); - - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), new Short(x) ); - - } - - public synchronized void updateString(int columnIndex, String x) throws SQLException - { - if ( Driver.logDebug ) Driver.debug("in update String "+fields[columnIndex-1].getName()+" = "+x); - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), x ); - - } - - public synchronized void updateTime(int columnIndex, Time x) throws SQLException - { - if ( Driver.logDebug ) Driver.debug("in update Time "+fields[columnIndex-1].getName()+" = "+x); - - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), x ); - - } - - public synchronized void updateTimestamp(int columnIndex, Timestamp x) throws SQLException - { - if ( Driver.logDebug ) Driver.debug("updating Timestamp "+fields[columnIndex-1].getName()+" = "+x); - - doingUpdates = !onInsertRow; - updateValues.put( fields[columnIndex-1].getName(), x ); - - - } - - public synchronized void updateNull(String columnName) throws SQLException - { - updateNull(findColumn(columnName)); - } - - /** - * JDBC 2.0 - * - * Update a column with a boolean value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateBoolean(String columnName, boolean x) throws SQLException - { - updateBoolean(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a byte value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateByte(String columnName, byte x) throws SQLException - { - updateByte(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a short value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateShort(String columnName, short x) throws SQLException - { - updateShort(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with an integer value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateInt(String columnName, int x) throws SQLException - { - updateInt(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a long value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateLong(String columnName, long x) throws SQLException - { - updateLong(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a float value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateFloat(String columnName, float x) throws SQLException - { - updateFloat(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a double value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateDouble(String columnName, double x) throws SQLException - { - updateDouble(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a BigDecimal value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateBigDecimal(String columnName, BigDecimal x) - throws SQLException - { - updateBigDecimal(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a String value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateString(String columnName, String x) throws SQLException - { - updateString(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a byte array value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateBytes(String columnName, byte x[]) throws SQLException - { - updateBytes(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a Date value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateDate(String columnName, java.sql.Date x) - throws SQLException - { - updateDate(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a Time value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateTime(String columnName, java.sql.Time x) - throws SQLException - { - updateTime(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with a Timestamp value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x) - throws SQLException - { - updateTimestamp(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with an ascii stream value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @param length of the stream - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateAsciiStream( - String columnName, - java.io.InputStream x, - int length) - throws SQLException - { - updateAsciiStream(findColumn(columnName), x, length); - } - - /** - * JDBC 2.0 - * - * Update a column with a binary stream value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @param length of the stream - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateBinaryStream( - String columnName, - java.io.InputStream x, - int length) - throws SQLException - { - updateBinaryStream(findColumn(columnName), x, length); - } - - /** - * JDBC 2.0 - * - * Update a column with a character stream value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @param length of the stream - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateCharacterStream( - String columnName, - java.io.Reader reader, - int length) - throws SQLException - { - updateCharacterStream(findColumn(columnName), reader,length); - } - - /** - * JDBC 2.0 - * - * Update a column with an Object value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @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 synchronized void updateObject(String columnName, Object x, int scale) - throws SQLException - { - updateObject(findColumn(columnName), x); - } - - /** - * JDBC 2.0 - * - * Update a column with an Object value. - * - * The updateXXX() methods are used to update column values in the - * current row, or the insert row. The updateXXX() methods do not - * update the underlying database, instead the updateRow() or insertRow() - * methods are called to update the database. - * - * @param columnName the name of the column - * @param x the new column value - * @exception SQLException if a database-access error occurs - */ - - public synchronized void updateObject(String columnName, Object x) throws SQLException - { - updateObject(findColumn(columnName), x); - } - - - - private int _findColumn( String columnName ) - { - int i; - - final int flen = fields.length; - for (i = 0 ; i < flen; ++i) - { - if (fields[i].getName().equalsIgnoreCase(columnName)) - { - return (i + 1); - } - } - return -1; - } - - - /** - * Is this ResultSet updateable? - */ - - boolean isUpdateable() throws SQLException - { - - if (updateable) return true; - - if ( Driver.logDebug ) Driver.debug("checking if rs is updateable"); - - parseQuery(); - - if ( singleTable == false ) - { - if ( Driver.logDebug ) Driver.debug("not a single table"); - return false; - } - - if ( Driver.logDebug ) Driver.debug("getting primary keys"); - - // - // Contains the primary key? - // - - primaryKeys = new Vector(); - - // this is not stricty jdbc spec, but it will make things much faster if used - // the user has to select oid, * from table and then we will just use oid - - - usingOID = false; - int oidIndex = _findColumn( "oid" ); - int i = 0; - - - // if we find the oid then just use it - - if ( oidIndex > 0 ) - { - i++; - primaryKeys.add( new PrimaryKey( oidIndex, "oid" ) ); - usingOID = true; - } - else - { - // otherwise go and get the primary keys and create a hashtable of keys - java.sql.ResultSet rs = ((java.sql.Connection)connection).getMetaData().getPrimaryKeys("","",tableName); - - - for( ; rs.next(); i++ ) - { - String columnName = rs.getString(4); // get the columnName - - int index = findColumn( columnName ); - - if ( index > 0 ) - { - primaryKeys.add( new PrimaryKey(index, columnName ) ); // get the primary key information - } - } - - rs.close(); - } - - numKeys = primaryKeys.size(); - - if ( Driver.logDebug ) Driver.debug( "no of keys=" + i ); - - if ( i < 1 ) - { - throw new SQLException("No Primary Keys"); - } - - updateable = primaryKeys.size() > 0; - - if ( Driver.logDebug ) Driver.debug( "checking primary key " + updateable ); - - return updateable; - } - - - /** - * - */ - public void parseQuery() - { - StringTokenizer st=new StringTokenizer(sqlQuery," \r\t"); - boolean tableFound=false, tablesChecked = false; - String name=""; - - singleTable = true; - - while ( !tableFound && !tablesChecked && st.hasMoreTokens() ) - { - name=st.nextToken(); - if ( !tableFound ) - { - if (name.toLowerCase().equals("from")) - { - tableName=st.nextToken(); - tableFound=true; - } - } - else - { - tablesChecked = true; - // if the very next token is , then there are multiple tables - singleTable = !name.equalsIgnoreCase(","); - } - } - } - - - private void updateRowBuffer() throws SQLException - { - - Enumeration columns = updateValues.keys(); - - while( columns.hasMoreElements() ) - { - String columnName = (String)columns.nextElement(); - int columnIndex = _findColumn( columnName ) - 1; - - switch ( connection.getSQLType( fields[columnIndex].getPGType() ) ) - { - - case Types.DECIMAL: - case Types.BIGINT: - case Types.DOUBLE: - case Types.BIT: - case Types.VARCHAR: - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: - case Types.SMALLINT: - case Types.FLOAT: - case Types.INTEGER: - case Types.CHAR: - case Types.NUMERIC: - case Types.REAL: - case Types.TINYINT: - - try - { - rowBuffer[columnIndex] = String.valueOf( updateValues.get( columnName ) ).getBytes(connection.getEncoding().name() ); - } - catch ( UnsupportedEncodingException ex) - { - throw new SQLException("Unsupported Encoding "+connection.getEncoding().name()); - } - case Types.NULL: - continue; - default: - rowBuffer[columnIndex] = (byte [])updateValues.get( columnName ); - } - - } - } - -} - -- GitLab