diff options
Diffstat (limited to 'src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java')
-rw-r--r-- | src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java | 176 |
1 files changed, 156 insertions, 20 deletions
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java index 2aeb5323db..bf91cf14c9 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.14 2002/11/20 07:34:32 barry Exp $ +/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.15 2003/02/04 09:20:08 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 @@ -19,12 +19,19 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme // The connection who created us protected AbstractJdbc1Connection connection; + public org.postgresql.PGConnection getPGConnection() { + return connection; + } + /** The warnings chain. */ protected SQLWarning warnings = null; /** Maximum number of rows to return, 0 = unlimited */ protected int maxrows = 0; + /** Number of rows to get in a batch. */ + protected int fetchSize = 0; + /** Timeout (in seconds) for a query (not used) */ protected int timeout = 0; @@ -47,8 +54,10 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme private String[] m_origSqlFragments; private String[] m_executeSqlFragments; protected Object[] m_binds = new Object[0]; + protected String[] m_bindTypes = new String[0]; - private String m_statementName = null; + protected String m_statementName = null; + private boolean m_useServerPrepare = false; private static int m_preparedCount = 1; @@ -67,6 +76,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme protected Object callResult; + public abstract java.sql.ResultSet createResultSet(org.postgresql.Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException; public AbstractJdbc1Statement (AbstractJdbc1Connection connection) { @@ -117,7 +127,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme } - + /* * Execute a SQL statement that retruns a single ResultSet * @@ -132,11 +142,21 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme m_binds = new Object[0]; //If we have already created a server prepared statement, we need //to deallocate the existing one - if (m_statementName != null) { - ((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName); - m_statementName = null; - m_origSqlFragments = null; - m_executeSqlFragments = null; + if (m_statementName != null) + { + try + { + ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName); + } + catch (Exception e) + { + } + finally + { + m_statementName = null; + m_origSqlFragments = null; + m_executeSqlFragments = null; + } } return executeQuery(); } @@ -150,7 +170,11 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme */ public java.sql.ResultSet executeQuery() throws SQLException { - this.execute(); + if (fetchSize > 0) + this.executeWithCursor(); + else + this.execute(); + while (result != null && !((AbstractJdbc1ResultSet)result).reallyResultSet()) result = ((AbstractJdbc1ResultSet)result).getNext(); if (result == null) @@ -175,7 +199,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme //If we have already created a server prepared statement, we need //to deallocate the existing one if (m_statementName != null) { - ((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName); + ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName); m_statementName = null; m_origSqlFragments = null; m_executeSqlFragments = null; @@ -219,7 +243,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme //If we have already created a server prepared statement, we need //to deallocate the existing one if (m_statementName != null) { - ((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName); + ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName); m_statementName = null; m_origSqlFragments = null; m_executeSqlFragments = null; @@ -317,7 +341,9 @@ 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(m_sqlFragments, m_binds, (java.sql.Statement)this); + result = org.postgresql.core.QueryExecutor.execute(m_sqlFragments, + m_binds, + (java.sql.Statement)this); //If we are executing a callable statement function set the return data if (isFunction) @@ -341,6 +367,102 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme } } + /** version of execute which converts the query to a cursor. + */ + public boolean executeWithCursor() throws SQLException + { + if (isFunction && !returnTypeSet) + throw new PSQLException("postgresql.call.noreturntype"); + if (isFunction) + { // set entry 1 to dummy entry.. + m_binds[0] = ""; // dummy entry which ensured that no one overrode + m_bindTypes[0] = PG_TEXT; + // and calls to setXXX (2,..) really went to first arg in a function call.. + } + + // New in 7.1, if we have a previous resultset then force it to close + // This brings us nearer to compliance, and helps memory management. + // Internal stuff will call ExecSQL directly, bypassing this. + if (result != null) + { + java.sql.ResultSet rs = getResultSet(); + if (rs != null) + rs.close(); + } + + // I've pretty much ignored server prepared statements... can declare and prepare be + // used together? + // It's trivial to change this: you just have to resolve this issue + // of how to work out whether there's a function call. If there isn't then the first + // element of the array must be the bit that you extend to become the cursor + // decleration. + // The last thing that can go wrong is when the user supplies a cursor statement + // directly: the translation takes no account of that. I think we should just look + // for declare and stop the translation if we find it. + + // The first thing to do is transform the statement text into the cursor form. + String[] origSqlFragments = m_sqlFragments; + m_sqlFragments = new String[origSqlFragments.length]; + System.arraycopy(origSqlFragments, 0, m_sqlFragments, 0, origSqlFragments.length); + // Pinch the prepared count for our own nefarious purposes. + m_statementName = "JDBC_CURS_" + m_preparedCount++; + // The static bit to prepend to all querys. + String cursDecl = "BEGIN; DECLARE " + m_statementName + " CURSOR FOR "; + String endCurs = " FETCH FORWARD " + fetchSize + " FROM " + m_statementName + ";"; + + // Add the real query to the curs decleration. + // This is the bit that really makes the presumption about + // m_sqlFragments not being a function call. + if (m_sqlFragments.length < 1) + m_sqlFragments[0] = cursDecl + "SELECT NULL;"; + + else if (m_sqlFragments.length < 2) + { + if (m_sqlFragments[0].endsWith(";")) + m_sqlFragments[0] = cursDecl + m_sqlFragments[0] + endCurs; + else + m_sqlFragments[0] = cursDecl + m_sqlFragments[0] + ";" + endCurs; + } + else + { + m_sqlFragments[0] = cursDecl + m_sqlFragments[0]; + if (m_sqlFragments[m_sqlFragments.length - 1].endsWith(";")) + m_sqlFragments[m_sqlFragments.length - 1] += endCurs; + else + m_sqlFragments[m_sqlFragments.length - 1] += (";" + endCurs); + } + + result = org.postgresql.core.QueryExecutor.execute(m_sqlFragments, + m_binds, + (java.sql.Statement)this); + + //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) + { + Object[] arr = + { "java.sql.Types=" + columnType, + "java.sql.Types=" + functionReturnType + }; + throw new PSQLException ("postgresql.call.wrongrtntype",arr); + } + result.close (); + return true; + } + else + { + return (result != null && ((AbstractJdbc1ResultSet)result).reallyResultSet()); + } + } + + /* * setCursorName defines the SQL cursor name that will be used by * subsequent execute methods. This name can then be used in SQL @@ -593,7 +715,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme // If using server prepared statements deallocate them if (m_useServerPrepare && m_statementName != null) { - ((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName); + ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName); } // Disasociate it from us (For Garbage Collection) @@ -1690,7 +1812,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme */ public byte[] getBytes(int parameterIndex) throws SQLException { - checkIndex (parameterIndex, Types.VARBINARY, "Bytes"); + checkIndex (parameterIndex, Types.VARBINARY, Types.BINARY, "Bytes"); return ((byte [])callResult); } @@ -1847,7 +1969,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme String l_sql = p_sql; int index = l_sql.indexOf ("="); // is implied func or proc? boolean isValid = true; - if (index != -1) + if (index > -1) { isFunction = true; isValid = l_sql.indexOf ("?") < index; // ? before = @@ -1875,12 +1997,25 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme // sure that the parameter numbers are the same as in the original // sql we add a dummy parameter in this case l_sql = (isFunction ? "?" : "") + l_sql.substring (index + 4); - l_sql = "select " + l_sql + " as " + RESULT_COLUMN + ";"; return l_sql; } /** helperfunction for the getXXX calls to check isFunction and index == 1 + * Compare BOTH type fields against the return type. + */ + protected void checkIndex (int parameterIndex, int type1, int type2, String getName) + throws SQLException + { + checkIndex (parameterIndex); + if (type1 != this.testReturn && type2 != this.testReturn) + throw new PSQLException("postgresql.call.wrongget", + new Object[]{"java.sql.Types=" + testReturn, + getName, + "java.sql.Types=" + type1}); + } + + /** helperfunction for the getXXX calls to check isFunction and index == 1 */ protected void checkIndex (int parameterIndex, int type, String getName) throws SQLException @@ -1888,10 +2023,11 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme checkIndex (parameterIndex); if (type != this.testReturn) throw new PSQLException("postgresql.call.wrongget", - new Object[]{"java.sql.Types=" + testReturn, - getName, - "java.sql.Types=" + type}); + 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 @@ -1912,7 +2048,7 @@ public abstract class AbstractJdbc1Statement implements org.postgresql.PGStateme //If turning server prepared statements off deallocate statement //and reset statement name if (m_useServerPrepare != flag && !flag) - ((AbstractJdbc1Connection)connection).ExecSQL("DEALLOCATE " + m_statementName); + ((AbstractJdbc1Connection)connection).execSQL("DEALLOCATE " + m_statementName); m_statementName = null; m_useServerPrepare = flag; } else { |