Modified: incubator/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java URL: http://svn.apache.org/viewcvs/incubator/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java?rev=165585&r1=165584&r2=165585&view=diff ============================================================================== --- incubator/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java (original) +++ incubator/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java Sun May 1 23:25:59 2005 @@ -19,1957 +19,2093 @@ */ package org.apache.derby.client.am; -import org.apache.derby.client.am.Section; -public class Statement implements java.sql.Statement, StatementCallbackInterface, UnitOfWorkListener -{ - // JDBC 3 constant indicating that the current ResultSet object - // should be closed when calling getMoreResults. - // Constant value matches that defined by JDBC 3 java.sql.Statement.CLOSE_CURRENT_RESULT - public final static int CLOSE_CURRENT_RESULT = 1; - - // JDBC 3 constant indicating that the current ResultSet object - // should not be closed when calling getMoreResults. - // Constant value matches that defined by JDBC 3 java.sql.Statement.KEEP_CURRENT_RESULT - public final static int KEEP_CURRENT_RESULT = 2; - - // JDBC 3 constant indicating that all ResultSet objects that - // have previously been kept open should be closed when calling getMoreResults. - // Constant value matches that defined by JDBC 3 java.sql.Statement.CLOSE_ALL_RESULTS - public final static int CLOSE_ALL_RESULTS = 3; - - //---------------------navigational members----------------------------------- - - public MaterialStatement materialStatement_ = null; - - public Connection connection_; - private SqlWarning warnings_ = null; - public Section section_; - public Agent agent_; - - public ResultSet resultSet_ = null; - - // Use -1, if there is no update count returned, ie. when result set is returned. 0 is a valid update count for DDL. - int updateCount_ = -1; - int returnValueFromProcedure_; - - // Enumeration of the flavors of statement execute call used. - static final int executeQueryMethod__ = 1; - static final int executeUpdateMethod__ = 2; - static final int executeMethod__ = 3; - - // sqlMode_ will be moved to PS as soon as we remove the hack reference in completeExecute() - // Enumerated in Statement: S.sqlIsQuery__, S.sqlIsCall__, S.sqlIsUpdate__ - // Determines whether sql_ starts with SELECT/VALUES, CALL, or other (assumed to be an update). - protected int sqlMode_ = 0; - // Enum for sqlMode_: - static final int isQuery__ = 0x1; // sql starts with SELECT.... or VALUES... - static final int isCall__ = 0x2; // sql starts with CALL ... - static final int isUpdate__ = 0x4; // All other sql is categorized as a update DML or DDL. - - // sqlUpdateMode_ is only set when the sqlMode_ == isUpdate__ - public int sqlUpdateMode_ = 0; - // Enum for sqlUpdateMode_: - public final static int isCommitSql__ = 0x1; - public final static int isRollbackSql__ = 0x2; - final static int isPositionedUpdateDeleteSql__ = 0x10; - final static int isInsertSql__ = 0x20; // used to recognize "insert" for auto-generated keys - final static int isDeleteSql__ = 0x40; // used to recognize "delete" for parsing cursorname - final static int isUpdateSql__ = 0x80; // used to recognize "update" for parsing cursorname - - - public ColumnMetaData resultSetMetaData_; // type information for output sqlda - - // these two are used during parsing of literals for call statement. - // please add a comment desribing what why you can't reuse inputs_ and parameterMetaData_ - // members for the literal inputs - - // Caching the Cursor object for reuse. - public Cursor cachedCursor_ = null; - public Cursor cachedSingletonRowData_ = null; - public boolean isPreparedStatement_ = false; - public boolean isCallableStatement_ = false; // we can get rid of this member once we define polymorphic reset() on S/PS/CS - - //---------------------navigational cheat-links------------------------------- - // Cheat-links are for convenience only, and are not part of the conceptual model. - // Warning: - // Cheat-links should only be defined for invariant state data. - // That is, state data that is set by the constructor and never changes. - - // Alias for connection_.databaseMetaData - public DatabaseMetaData databaseMetaData_; - - //-----------------------------state------------------------------------------ - - // Jdbc 1 positioned updates are implemented via - // sql scan for "...where current of <users-cursor-name>", - // the addition of mappings from cursor names to query sections, - // and the subtitution of <users-cursor-name> with <canned-cursor-name> in the pass-thru sql string - // "...where current of <canned-cursor-name>" when user-defined cursor names are used. - // Both "canned" cursor names (from our jdbc package set) and user-defined cursor names are mapped. - // Statement.cursorName_ is initialized to null until the cursor name is requested or set. - // When set (s.setCursorName()) with a user-defined name, then it is added to the cursor map at that time; - // When requested (rs.getCursorName()), if the cursor name is still null, - // then is given the canned cursor name as defined by our jdbc package set and added to the cursor map. - // Still need to consider how positioned updates should interact with multiple result sets from a stored. - String cursorName_ = null; - - // This means the client-side jdbc statement object is open. - // This value is set to true when the statement object is constructed, and will not change - // until statement.close() is called either directly or via connection.close(), finalizer, or other methods. - boolean openOnClient_ = true; - // This means a DERBY server-side section for this statement is in the prepared state. - // A client-side jdbc statement may remain open across commits (openOnClient=true), - // but the server-side DERBY section moves to an unprepared state (openOnServer=false) across commits, - // requiring an implicit re-prepare "under the covers" by the driver. - // Unprepared jdbc query statements still have prepared sections on the server. - // This openOnServer_ only has implications for preparedstatement - boolean openOnServer_ = false; - - - //private int indexOfCurrentResultSet_ = -1; - protected int indexOfCurrentResultSet_ = -1; - ResultSet [] resultSetList_ = null; // array of ResultSet objects - - int timeout_ = 0; // for query timeout in seconds, multiplied by 1000 when passed to java.util.Timer - int maxRows_ = 0; - int maxFieldSize_ = 0; // zero means that there is no limit to the size of a column. - boolean escapedProcedureCallWithResult_ = false; - - // When this is false we skip autocommit for this PreparedStatement. - // This is needed when the PreparedStatement object is used internally by - // the driver and a commit is not desired, e.g., Blob/Clob API calls - public boolean isAutoCommittableStatement_ = true; - - // The user has no control over the statement that owns a catalog query, and has no ability to close that statement. - // We need a special member variable on our internal catalog query statements so that - // when the catalog query is closed, the result set will know to close it's owning statement. - boolean isCatalogQuery_ = false; - - - // This collection is used for two different purposes: - // For statement batching it contains the batched SQL strings. - // For prepared statement batching it contains the batched input rows. - java.util.ArrayList batch_ = new java.util.ArrayList(); - - - // Scrollable cursor attributes - public int resultSetType_ = java.sql.ResultSet.TYPE_FORWARD_ONLY; - public int resultSetConcurrency_ = java.sql.ResultSet.CONCUR_READ_ONLY; - public int resultSetHoldability_; - // This is ignored by the driver if this is zero. - // For the net forward-only result set, if fetchSize is unset, we let the server return however many rows will fit in a query block. - // For the net scrollable result set, then we use a default of 64 rows. - public int fetchSize_ = 0; - public int fetchDirection_ = java.sql.ResultSet.FETCH_FORWARD; - - // Conceptually this doesn't belong in Statement, but belongs in PreparedStatement, - // since Statement doesn't know about params, so we're just putting it here perhaps temporarily, - // Used for callable statement OUT paramters. - public Cursor singletonRowData_ = null; - - // number of invisible result sets returned from a stored procedure. - public int numInvisibleRS_ = 0; - - // This is a cache of the attributes to be sent on prepare. - // Think about caching the entire prepare DDM string for the re-prepares - public String cursorAttributesToSendOnPrepare_ = null; - - // The following members are for the exclusive use of prepared statements that require auto-generated keys to be returned - public PreparedStatement preparedStatementForAutoGeneratedKeys_; - public ResultSet generatedKeysResultSet_; - public String[] generatedKeysColumnNames_; - public int autoGeneratedKeys_ = java.sql.Statement.NO_GENERATED_KEYS; - - // This flag makes sure that only one copy of this statement - // will be in connection_.commitListeners_. - - - //---------------------constructors/finalizer--------------------------------- - - private Statement() throws SqlException - { - initStatement(); - } - - private void resetStatement() throws SqlException - { - initStatement(); - } - - private void initStatement() throws SqlException - { - materialStatement_ = null; - connection_ = null; - agent_ = null; - databaseMetaData_ = null; - resultSetType_ = java.sql.ResultSet.TYPE_FORWARD_ONLY; - resultSetConcurrency_ = java.sql.ResultSet.CONCUR_READ_ONLY; - resultSetHoldability_ = 0; - cursorAttributesToSendOnPrepare_ = null; - - initResetStatement(); - } - - private void initResetStatement() throws SqlException - { - initResetPreparedStatement(); - - //section_ = null; // don't set section to null because write piggyback command require a section - if (section_ != null) - section_.free(); - sqlMode_ = 0; - sqlUpdateMode_ = 0; - resultSetMetaData_ = null; - } - - protected void initResetPreparedStatement () - { - warnings_ = null; - //section_ = null; - resultSet_ = null; - updateCount_ = -1; - returnValueFromProcedure_ = 0; - cursorName_ = null; - openOnClient_ = true; - openOnServer_ = false; - indexOfCurrentResultSet_ = -1; - resultSetList_ = null; - timeout_ = 0; - maxRows_ = 0; - maxFieldSize_ = 0; - escapedProcedureCallWithResult_ = false; - isCatalogQuery_ = false; - isAutoCommittableStatement_ = true; - - if (batch_ == null) - batch_ = new java.util.ArrayList(); - else - batch_.clear(); - fetchSize_ = 0; - fetchDirection_ = java.sql.ResultSet.FETCH_FORWARD; - singletonRowData_ = null; - numInvisibleRS_ = 0; - preparedStatementForAutoGeneratedKeys_ = null; - generatedKeysResultSet_ = null; - generatedKeysColumnNames_ = null; - autoGeneratedKeys_ = java.sql.Statement.NO_GENERATED_KEYS; - - // these members were not initialized - isPreparedStatement_ = false; - } - - // If a dataSource is passed into resetClientConnection(), then we will assume - // properties on the dataSource may have changed, and we will need to go through - // the open-statement list on the connection and do a full reset on all statements, - // including preparedStatement's and callableStatement's. This is because property - // change may influence the section we allocate for the preparedStatement, and - // also the cursor attributes, i.e. setCursorSensitivity(). - // If no dataSource is passed into resetClientConnection(), then we will do the - // minimum reset required for preparedStatement's and callableStatement's. - public void reset (boolean fullReset) throws SqlException - { - if (fullReset) - connection_.resetStatement (this); - else { - initResetStatement(); - materialStatement_.reset_(); - } - } - - public Statement (Agent agent, Connection connection) throws SqlException - { - this(); - initStatement (agent, connection); - } - - public void resetStatement (Agent agent, Connection connection) throws SqlException - { - resetStatement(); - initStatement (agent, connection); - } - - private void initStatement (Agent agent, Connection connection) - { - agent_ = agent; - connection_ = connection; - databaseMetaData_ = connection.databaseMetaData_; - } - - // For jdbc 2 statements with scroll attributes - public Statement (Agent agent, Connection connection, int type, int concurrency, int holdability, - int autoGeneratedKeys, String[] columnNames) throws SqlException - { - this (agent, connection); - initStatement (type, concurrency, holdability, autoGeneratedKeys, columnNames); - } - - public void resetStatement (Agent agent, Connection connection, int type, int concurrency, int holdability, - int autoGeneratedKeys, String[] columnNames) throws SqlException - { - resetStatement (agent, connection); - initStatement (type, concurrency, holdability, autoGeneratedKeys, columnNames); - } - - private void initStatement (int type, int concurrency, int holdability, - int autoGeneratedKeys, String[] columnNames) throws SqlException - { - switch (type) { - case java.sql.ResultSet.TYPE_FORWARD_ONLY: - case java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE: - case java.sql.ResultSet.TYPE_SCROLL_SENSITIVE: - resultSetType_ = type; - break; - default: - throw new SqlException (agent_.logWriter_, "Invalid argument: " + - "ResultSet Type " + type + " is invalid."); - } - - switch (concurrency) { - case java.sql.ResultSet.CONCUR_READ_ONLY: - case java.sql.ResultSet.CONCUR_UPDATABLE: - resultSetConcurrency_ = concurrency; - break; - default: - throw new SqlException (agent_.logWriter_, "Invalid argument: " + - "ResultSet Concurrency " + concurrency + " is invalid."); - } - - switch (holdability) { - case org.apache.derby.jdbc.ClientDataSource.CLOSE_CURSORS_AT_COMMIT: - case org.apache.derby.jdbc.ClientDataSource.HOLD_CURSORS_OVER_COMMIT: - resultSetHoldability_ = holdability; - break; - default: - throw new SqlException (agent_.logWriter_, "Invalid argument: " + - "ResultSet holdability " + holdability + " is invalid."); - } - - switch (autoGeneratedKeys) { - case java.sql.Statement.NO_GENERATED_KEYS: - case java.sql.Statement.RETURN_GENERATED_KEYS: - autoGeneratedKeys_ = autoGeneratedKeys; - break; - default: - throw new SqlException (agent_.logWriter_, "Invalid argument: " + - "Statement auto-generated keys value " + autoGeneratedKeys + - " is invalid."); - } - - generatedKeysColumnNames_ = columnNames; - } - - protected void finalize () throws java.lang.Throwable - { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "finalize"); - if (openOnClient_) { - synchronized (connection_) { - closeX(); - } - } - super.finalize(); - } - - // ---------------------------jdbc 1------------------------------------------ - - public java.sql.ResultSet executeQuery (String sql) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "executeQuery", sql); - ResultSet resultSet = executeQueryX (sql); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "executeQuery", resultSet); - return resultSet; - } - } - - private ResultSet executeQueryX (String sql) throws SqlException - { - flowExecute (executeQueryMethod__, sql); - - checkExecuteQueryPostConditions ("java.sql.Statement"); - return resultSet_; - } - - void checkExecuteQueryPostConditions (String jdbcStatementInterfaceName) throws SqlException - { - // We'll just rely on finalizers to close the dangling result sets. - if (resultSetList_ != null && resultSetList_.length != 1) - throw new SqlException (agent_.logWriter_, jdbcStatementInterfaceName + ".executeQuery() cannot be called " + - "because multiple result sets were returned." + - " Use " + jdbcStatementInterfaceName + ".execute() to obtain multiple results."); - - if (resultSet_ == null) - throw new SqlException (agent_.logWriter_, jdbcStatementInterfaceName + ".executeQuery() was called " + - "but no result set was returned."+ - " Use " + jdbcStatementInterfaceName + ".executeUpdate() for non-queries."); - } - - public int executeUpdate (String sql) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "executeUpdate", sql); - int updateValue = executeUpdateX (sql); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "executeUpdate", updateValue); - return updateValue; - } - } - private int executeUpdateX (String sql) throws SqlException - { - flowExecute (executeUpdateMethod__, sql); - - checkExecuteUpdatePostConditions ("java.sql.Statement"); - return updateCount_; - } - - void checkExecuteUpdatePostConditions (String jdbcStatementInterfaceName) throws SqlException - { - // We'll just rely on finalizers to close the dangling result sets. - if (resultSetList_ != null) - throw new SqlException (agent_.logWriter_, jdbcStatementInterfaceName + ".executeUpdate() cannot be called " + - "because multiple result sets returned." + - " Use " + jdbcStatementInterfaceName + ".execute() to obtain multiple results."); - - // We'll just rely on the finalizer to close the dangling result set. - if (resultSet_ != null) - throw new SqlException (agent_.logWriter_, jdbcStatementInterfaceName + ".executeUpdate() was called " + - "but a result set was returned." + - " Use " + jdbcStatementInterfaceName + ".executeQuery() to obtain a result set."); - } - - // The server holds statement resources until transaction end. - public void close () throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "close"); - closeX(); - } - } - - // An untraced version of close() - public void closeX () throws SqlException - { - if (!openOnClient_) return; - // Regardless of whether or not this statement is in the prepared state, - // we need to close any open cursors for this statement on the server. - int numberOfResultSetsToClose = (resultSetList_ == null) ? 0 : resultSetList_.length; - boolean willTickleServer = willTickleServer (numberOfResultSetsToClose, true); - try { - if (willTickleServer) - flowClose (); - else - flowCloseOutsideUOW (); - } - finally { - markClosed(); - connection_.openStatements_.remove (this); - } - // push the mark close of rsmd into Statement.markClosed() method - if (resultSetMetaData_ != null) { - resultSetMetaData_.markClosed(); - resultSetMetaData_ = null; - } - } - - public int getMaxFieldSize () throws SqlException - { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "getMaxFieldSize"); - checkForClosedStatement (); - return maxFieldSize_; - } - - public void setMaxFieldSize (int max) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "setMaxFieldSize", max); - checkForClosedStatement (); - if (max < 0) throw new SqlException (agent_.logWriter_, "Invalid maxFieldSize value: " + max); - maxFieldSize_ = max; - } - } - - public int getMaxRows () throws SqlException - { - checkForClosedStatement (); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getMaxRows", maxRows_); - return maxRows_; - } - - public void setMaxRows (int maxRows) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "setMaxRows", maxRows); - checkForClosedStatement (); // Per jdbc spec (see java.sql.Statement.close() javadoc) - if (maxRows < 0) throw new SqlException (agent_.logWriter_, "Invalid maxRows value: " + maxRows); - maxRows_ = maxRows; - } - } - - public void setEscapeProcessing (boolean enable) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "setEscapeProcessing", enable); - checkForClosedStatement (); // Per jdbc spec (see java.sql.Statement.close() javadoc) - } - } - - public int getQueryTimeout () throws SqlException - { - checkForClosedStatement (); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getQueryTimeout", timeout_); - return timeout_; - } - - public void setQueryTimeout (int seconds) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "setQueryTimeout", seconds); - checkForClosedStatement (); // Per jdbc spec (see java.sql.Statement.close() javadoc) - if (seconds < 0) throw new SqlException (agent_.logWriter_, "Attempt to set a negative query timeout"); - timeout_ = seconds; // java.util.Timer takes milliseconds - } - } - - public void cancel () throws SqlException - { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "cancel"); - checkForClosedStatement (); // Per jdbc spec (see java.sql.Statement.close() javadoc) - throw new SqlException (agent_.logWriter_, "cancel() not supported by server"); - } - - public java.sql.SQLWarning getWarnings () throws SqlException - { - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getWarnings", warnings_); - return warnings_; - } - - public void clearWarnings () throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "clearWarnings"); - clearWarningsX(); - } - } - - // An untraced version of clearWarnings() - public void clearWarningsX () - { - warnings_ = null; - } - - // Dnc statements are already associated with a unique cursor name as defined - // by our canned dnc package set. - // ResultSet.getCursorName() should be used to - // obtain the for update cursor name to use when executing a positioned update statement. - // See Jdbc 3 spec section 14.2.4.4. - public void setCursorName (String name) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "setCursorName", name); - checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) - if (name == null || name.equals ("")) throw new SqlException (agent_.logWriter_, "Invalid cursor name."); - - // Invalid to set the cursor name if there are ResultSet's open on the Statement. - if (resultSet_ != null && resultSet_.openOnClient_) - throw new SqlException (agent_.logWriter_, "Invalid operation: setCursorName() " + - "called when there are open ResultSet's on the Statement."); - - // Duplicate cursor names not allowed. - if (connection_.clientCursorNameCache_.containsKey (name)) - throw new SqlException (agent_.logWriter_, "Duplicate cursor names are not allowed."); - connection_.clientCursorNameCache_.put (name, name); - - // section_ is null for Statement objects. We will defer the mapping of cursorName - // to section until when the query is executed. - if (section_ != null) { - agent_.sectionManager_.mapCursorNameToQuerySection (name, (Section) section_); - - // This means we must subtitute the <users-cursor-name> with the <canned-cursor-name> - // in the pass-thru sql string "...where current of <canned-cursor-name>". - section_.setClientCursorName (name); - } - cursorName_ = name; - } - } - - //----------------------- Multiple Results -------------------------- - - - public boolean execute (String sql) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "execute", sql); - boolean b = executeX (sql); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "execute", b); - return b; - } - } - - boolean executeX (String sql) throws SqlException - { - flowExecute (executeMethod__, sql); - return resultSet_ != null; - } - - public java.sql.ResultSet getResultSet () throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "getResultSet"); - checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getResultSet", resultSet_); - return resultSet_; - } - } - - public int getUpdateCount () throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "getUpdateCount"); - checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getUpdateCount", updateCount_); - return updateCount_; - } - } - - public boolean getMoreResults () throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "getMoreResults"); - boolean resultIsResultSet = getMoreResultsX (CLOSE_ALL_RESULTS); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getMoreResults", resultIsResultSet); - return resultIsResultSet; - } - } - - //--------------------------JDBC 2.0----------------------------- - - public void setFetchDirection (int direction) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "setFetchDirection", direction); - checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) - switch (direction) { - case java.sql.ResultSet.FETCH_FORWARD: - case java.sql.ResultSet.FETCH_REVERSE: - case java.sql.ResultSet.FETCH_UNKNOWN: - fetchDirection_ = direction; - break; - default: - throw new SqlException (agent_.logWriter_, "Invalid fetch direction " + direction); - } - } - } - - public int getFetchDirection () throws SqlException - { - checkForClosedStatement (); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getFetchDirection", fetchDirection_); - return fetchDirection_; - } - - public void setFetchSize (int rows) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "setFetchSize", rows); - checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) - - if (rows < 0 || (maxRows_ != 0 && rows > maxRows_)) - throw new SqlException (agent_.logWriter_, "Invalid fetch size " + rows); - fetchSize_ = rows; - } - } - - public int getFetchSize () throws SqlException - { - checkForClosedStatement (); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getFetchSize", fetchSize_); - return fetchSize_; - } - - public int getResultSetConcurrency () throws SqlException - { - checkForClosedStatement (); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getResultSetConcurrency", resultSetConcurrency_); - return resultSetConcurrency_; - } - - public int getResultSetType () throws SqlException - { - checkForClosedStatement (); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getResultSetType", resultSetType_); - return resultSetType_; - } - - public void addBatch (String sql) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "addBatch", sql); - checkForClosedStatement (); - sql = connection_.nativeSQLX (sql); - batch_.add (sql); - } - } - - public void clearBatch () throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "clearBatch"); - checkForClosedStatement (); - batch_.clear (); - } - } - - public int[] executeBatch () throws SqlException, BatchUpdateException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "executeBatch"); - int [] updateCounts = executeBatchX(); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "executeBatch", updateCounts); - return updateCounts; - } - } - - private int[] executeBatchX () throws SqlException, BatchUpdateException - { - checkForClosedStatement (); // Per jdbc spec (see java.sql.Statement.close() javadoc) - clearWarningsX (); // Per jdbc spec 0.7, and getWarnings() javadoc - resultSetList_ = null; - - // Initialize all the updateCounts to indicate failure - // This is done to account for "chain-breaking" errors where we cannot - // read any more replies - int[] updateCounts = new int[batch_.size()]; - for (int i = 0; i < batch_.size(); i++){ - updateCounts[i] = -3; - } - flowExecuteBatch (updateCounts); - - return updateCounts; - } - - public java.sql.Connection getConnection () throws SqlException - { - checkForClosedStatement (); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getConnection", connection_); - return connection_; - } - - //--------------------------JDBC 3.0----------------------------- - - public boolean getMoreResults (int current) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "getMoreResults", current); - boolean resultIsResultSet = getMoreResultsX (current); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getMoreResults", resultIsResultSet); - return resultIsResultSet; - } - } - - private boolean getMoreResultsX (int current) throws SqlException - { - checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) - boolean resultIsResultSet; - updateCount_ = -1; - if (resultSetList_ == null) { - if (resultSet_ != null) { - if (current != KEEP_CURRENT_RESULT) resultSet_.closeX(); +public class Statement implements java.sql.Statement, StatementCallbackInterface, UnitOfWorkListener { + + // JDBC 3 constant indicating that the current ResultSet object + // should be closed when calling getMoreResults. + // Constant value matches that defined by JDBC 3 java.sql.Statement.CLOSE_CURRENT_RESULT + public final static int CLOSE_CURRENT_RESULT = 1; + + // JDBC 3 constant indicating that the current ResultSet object + // should not be closed when calling getMoreResults. + // Constant value matches that defined by JDBC 3 java.sql.Statement.KEEP_CURRENT_RESULT + public final static int KEEP_CURRENT_RESULT = 2; + + // JDBC 3 constant indicating that all ResultSet objects that + // have previously been kept open should be closed when calling getMoreResults. + // Constant value matches that defined by JDBC 3 java.sql.Statement.CLOSE_ALL_RESULTS + public final static int CLOSE_ALL_RESULTS = 3; + + //---------------------navigational members----------------------------------- + + public MaterialStatement materialStatement_ = null; + + public Connection connection_; + private SqlWarning warnings_ = null; + public Section section_; + public Agent agent_; + + public ResultSet resultSet_ = null; + + // Use -1, if there is no update count returned, ie. when result set is returned. 0 is a valid update count for DDL. + int updateCount_ = -1; + int returnValueFromProcedure_; + + // Enumeration of the flavors of statement execute call used. + static final int executeQueryMethod__ = 1; + static final int executeUpdateMethod__ = 2; + static final int executeMethod__ = 3; + + // sqlMode_ will be moved to PS as soon as we remove the hack reference in completeExecute() + // Enumerated in Statement: S.sqlIsQuery__, S.sqlIsCall__, S.sqlIsUpdate__ + // Determines whether sql_ starts with SELECT/VALUES, CALL, or other (assumed to be an update). + protected int sqlMode_ = 0; + // Enum for sqlMode_: + static final int isQuery__ = 0x1; // sql starts with SELECT.... or VALUES... + static final int isCall__ = 0x2; // sql starts with CALL ... + static final int isUpdate__ = 0x4; // All other sql is categorized as a update DML or DDL. + + // sqlUpdateMode_ is only set when the sqlMode_ == isUpdate__ + public int sqlUpdateMode_ = 0; + // Enum for sqlUpdateMode_: + public final static int isCommitSql__ = 0x1; + public final static int isRollbackSql__ = 0x2; + final static int isPositionedUpdateDeleteSql__ = 0x10; + final static int isInsertSql__ = 0x20; // used to recognize "insert" for auto-generated keys + final static int isDeleteSql__ = 0x40; // used to recognize "delete" for parsing cursorname + final static int isUpdateSql__ = 0x80; // used to recognize "update" for parsing cursorname + + + public ColumnMetaData resultSetMetaData_; // type information for output sqlda + + // these two are used during parsing of literals for call statement. + // please add a comment desribing what why you can't reuse inputs_ and parameterMetaData_ + // members for the literal inputs + + // Caching the Cursor object for reuse. + public Cursor cachedCursor_ = null; + public Cursor cachedSingletonRowData_ = null; + public boolean isPreparedStatement_ = false; + public boolean isCallableStatement_ = false; // we can get rid of this member once we define polymorphic reset() on S/PS/CS + + //---------------------navigational cheat-links------------------------------- + // Cheat-links are for convenience only, and are not part of the conceptual model. + // Warning: + // Cheat-links should only be defined for invariant state data. + // That is, state data that is set by the constructor and never changes. + + // Alias for connection_.databaseMetaData + public DatabaseMetaData databaseMetaData_; + + //-----------------------------state------------------------------------------ + + // Jdbc 1 positioned updates are implemented via + // sql scan for "...where current of <users-cursor-name>", + // the addition of mappings from cursor names to query sections, + // and the subtitution of <users-cursor-name> with <canned-cursor-name> in the pass-thru sql string + // "...where current of <canned-cursor-name>" when user-defined cursor names are used. + // Both "canned" cursor names (from our jdbc package set) and user-defined cursor names are mapped. + // Statement.cursorName_ is initialized to null until the cursor name is requested or set. + // When set (s.setCursorName()) with a user-defined name, then it is added to the cursor map at that time; + // When requested (rs.getCursorName()), if the cursor name is still null, + // then is given the canned cursor name as defined by our jdbc package set and added to the cursor map. + // Still need to consider how positioned updates should interact with multiple result sets from a stored. + String cursorName_ = null; + + // This means the client-side jdbc statement object is open. + // This value is set to true when the statement object is constructed, and will not change + // until statement.close() is called either directly or via connection.close(), finalizer, or other methods. + boolean openOnClient_ = true; + // This means a DERBY server-side section for this statement is in the prepared state. + // A client-side jdbc statement may remain open across commits (openOnClient=true), + // but the server-side DERBY section moves to an unprepared state (openOnServer=false) across commits, + // requiring an implicit re-prepare "under the covers" by the driver. + // Unprepared jdbc query statements still have prepared sections on the server. + // This openOnServer_ only has implications for preparedstatement + boolean openOnServer_ = false; + + + //private int indexOfCurrentResultSet_ = -1; + protected int indexOfCurrentResultSet_ = -1; + ResultSet[] resultSetList_ = null; // array of ResultSet objects + + int timeout_ = 0; // for query timeout in seconds, multiplied by 1000 when passed to java.util.Timer + int maxRows_ = 0; + int maxFieldSize_ = 0; // zero means that there is no limit to the size of a column. + boolean escapedProcedureCallWithResult_ = false; + + // When this is false we skip autocommit for this PreparedStatement. + // This is needed when the PreparedStatement object is used internally by + // the driver and a commit is not desired, e.g., Blob/Clob API calls + public boolean isAutoCommittableStatement_ = true; + + // The user has no control over the statement that owns a catalog query, and has no ability to close that statement. + // We need a special member variable on our internal catalog query statements so that + // when the catalog query is closed, the result set will know to close it's owning statement. + boolean isCatalogQuery_ = false; + + + // This collection is used for two different purposes: + // For statement batching it contains the batched SQL strings. + // For prepared statement batching it contains the batched input rows. + java.util.ArrayList batch_ = new java.util.ArrayList(); + + + // Scrollable cursor attributes + public int resultSetType_ = java.sql.ResultSet.TYPE_FORWARD_ONLY; + public int resultSetConcurrency_ = java.sql.ResultSet.CONCUR_READ_ONLY; + public int resultSetHoldability_; + // This is ignored by the driver if this is zero. + // For the net forward-only result set, if fetchSize is unset, we let the server return however many rows will fit in a query block. + // For the net scrollable result set, then we use a default of 64 rows. + public int fetchSize_ = 0; + public int fetchDirection_ = java.sql.ResultSet.FETCH_FORWARD; + + // Conceptually this doesn't belong in Statement, but belongs in PreparedStatement, + // since Statement doesn't know about params, so we're just putting it here perhaps temporarily, + // Used for callable statement OUT paramters. + public Cursor singletonRowData_ = null; + + // number of invisible result sets returned from a stored procedure. + public int numInvisibleRS_ = 0; + + // This is a cache of the attributes to be sent on prepare. + // Think about caching the entire prepare DDM string for the re-prepares + public String cursorAttributesToSendOnPrepare_ = null; + + // The following members are for the exclusive use of prepared statements that require auto-generated keys to be returned + public PreparedStatement preparedStatementForAutoGeneratedKeys_; + public ResultSet generatedKeysResultSet_; + public String[] generatedKeysColumnNames_; + public int autoGeneratedKeys_ = java.sql.Statement.NO_GENERATED_KEYS; + + // This flag makes sure that only one copy of this statement + // will be in connection_.commitListeners_. + + + //---------------------constructors/finalizer--------------------------------- + + private Statement() throws SqlException { + initStatement(); + } + + private void resetStatement() throws SqlException { + initStatement(); + } + + private void initStatement() throws SqlException { + materialStatement_ = null; + connection_ = null; + agent_ = null; + databaseMetaData_ = null; + resultSetType_ = java.sql.ResultSet.TYPE_FORWARD_ONLY; + resultSetConcurrency_ = java.sql.ResultSet.CONCUR_READ_ONLY; + resultSetHoldability_ = 0; + cursorAttributesToSendOnPrepare_ = null; + + initResetStatement(); + } + + private void initResetStatement() throws SqlException { + initResetPreparedStatement(); + + //section_ = null; // don't set section to null because write piggyback command require a section + if (section_ != null) { + section_.free(); + } + sqlMode_ = 0; + sqlUpdateMode_ = 0; + resultSetMetaData_ = null; + } + + protected void initResetPreparedStatement() { + warnings_ = null; + //section_ = null; resultSet_ = null; - } - resultIsResultSet = false; + updateCount_ = -1; + returnValueFromProcedure_ = 0; + cursorName_ = null; + openOnClient_ = true; + openOnServer_ = false; + indexOfCurrentResultSet_ = -1; + resultSetList_ = null; + timeout_ = 0; + maxRows_ = 0; + maxFieldSize_ = 0; + escapedProcedureCallWithResult_ = false; + isCatalogQuery_ = false; + isAutoCommittableStatement_ = true; + + if (batch_ == null) { + batch_ = new java.util.ArrayList(); + } else { + batch_.clear(); + } + fetchSize_ = 0; + fetchDirection_ = java.sql.ResultSet.FETCH_FORWARD; + singletonRowData_ = null; + numInvisibleRS_ = 0; + preparedStatementForAutoGeneratedKeys_ = null; + generatedKeysResultSet_ = null; + generatedKeysColumnNames_ = null; + autoGeneratedKeys_ = java.sql.Statement.NO_GENERATED_KEYS; + + // these members were not initialized + isPreparedStatement_ = false; + } + + // If a dataSource is passed into resetClientConnection(), then we will assume + // properties on the dataSource may have changed, and we will need to go through + // the open-statement list on the connection and do a full reset on all statements, + // including preparedStatement's and callableStatement's. This is because property + // change may influence the section we allocate for the preparedStatement, and + // also the cursor attributes, i.e. setCursorSensitivity(). + // If no dataSource is passed into resetClientConnection(), then we will do the + // minimum reset required for preparedStatement's and callableStatement's. + public void reset(boolean fullReset) throws SqlException { + if (fullReset) { + connection_.resetStatement(this); + } else { + initResetStatement(); + materialStatement_.reset_(); + } } - else { - if (numInvisibleRS_ == 0 && - current == CLOSE_CURRENT_RESULT && - resultSetList_[indexOfCurrentResultSet_] != null) { - resultSetList_[indexOfCurrentResultSet_].closeX(); - } - resultIsResultSet = indexOfCurrentResultSet_ + 1 < resultSetList_.length; - } - if ((current == CLOSE_ALL_RESULTS) && (numInvisibleRS_ == 0)) { - int numberOfResultSetsToClose = (resultSetList_ == null) ? 0 : indexOfCurrentResultSet_+1; - boolean willTickleServer = willTickleServer (numberOfResultSetsToClose, false); - if (willTickleServer) - flowCloseRetrievedResultSets(); - else - flowCloseRetrievedResultSetsOutsideUOW(); - } - if (resultIsResultSet) - resultSet_ = resultSetList_[++indexOfCurrentResultSet_]; - else - resultSet_ = null; - return resultIsResultSet; - } + public Statement(Agent agent, Connection connection) throws SqlException { + this(); + initStatement(agent, connection); + } + + public void resetStatement(Agent agent, Connection connection) throws SqlException { + resetStatement(); + initStatement(agent, connection); + } + + private void initStatement(Agent agent, Connection connection) { + agent_ = agent; + connection_ = connection; + databaseMetaData_ = connection.databaseMetaData_; + } + + // For jdbc 2 statements with scroll attributes + public Statement(Agent agent, Connection connection, int type, int concurrency, int holdability, + int autoGeneratedKeys, String[] columnNames) throws SqlException { + this(agent, connection); + initStatement(type, concurrency, holdability, autoGeneratedKeys, columnNames); + } + + public void resetStatement(Agent agent, Connection connection, int type, int concurrency, int holdability, + int autoGeneratedKeys, String[] columnNames) throws SqlException { + resetStatement(agent, connection); + initStatement(type, concurrency, holdability, autoGeneratedKeys, columnNames); + } + + private void initStatement(int type, int concurrency, int holdability, + int autoGeneratedKeys, String[] columnNames) throws SqlException { + switch (type) { + case java.sql.ResultSet.TYPE_FORWARD_ONLY: + case java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE: + case java.sql.ResultSet.TYPE_SCROLL_SENSITIVE: + resultSetType_ = type; + break; + default: + throw new SqlException(agent_.logWriter_, "Invalid argument: " + + "ResultSet Type " + type + " is invalid."); + } - public java.sql.ResultSet getGeneratedKeys () throws SqlException - { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "getGeneratedKeys"); - checkForClosedStatement (); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "getGeneratedKeys", generatedKeysResultSet_); - return generatedKeysResultSet_; + switch (concurrency) { + case java.sql.ResultSet.CONCUR_READ_ONLY: + case java.sql.ResultSet.CONCUR_UPDATABLE: + resultSetConcurrency_ = concurrency; + break; + default: + throw new SqlException(agent_.logWriter_, "Invalid argument: " + + "ResultSet Concurrency " + concurrency + " is invalid."); + } + + switch (holdability) { + case org.apache.derby.jdbc.ClientDataSource.CLOSE_CURSORS_AT_COMMIT: + case org.apache.derby.jdbc.ClientDataSource.HOLD_CURSORS_OVER_COMMIT: + resultSetHoldability_ = holdability; + break; + default: + throw new SqlException(agent_.logWriter_, "Invalid argument: " + + "ResultSet holdability " + holdability + " is invalid."); + } + + switch (autoGeneratedKeys) { + case java.sql.Statement.NO_GENERATED_KEYS: + case java.sql.Statement.RETURN_GENERATED_KEYS: + autoGeneratedKeys_ = autoGeneratedKeys; + break; + default: + throw new SqlException(agent_.logWriter_, "Invalid argument: " + + "Statement auto-generated keys value " + autoGeneratedKeys + + " is invalid."); + } + + generatedKeysColumnNames_ = columnNames; } - public int executeUpdate (String sql, int autoGeneratedKeys) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "executeUpdate", sql, autoGeneratedKeys); - autoGeneratedKeys_ = autoGeneratedKeys; - int updateValue = executeUpdateX (sql); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "executeUpdate", updateValue); - return updateValue; - } + protected void finalize() throws java.lang.Throwable { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "finalize"); + } + if (openOnClient_) { + synchronized (connection_) { + closeX(); + } + } + super.finalize(); } - public int executeUpdate (String sql, int columnIndexes[]) throws SqlException - { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "executeUpdate", sql, columnIndexes); - checkForClosedStatement (); - throw new SqlException (agent_.logWriter_, "Driver not capable"); + // ---------------------------jdbc 1------------------------------------------ + + public java.sql.ResultSet executeQuery(String sql) throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "executeQuery", sql); + } + ResultSet resultSet = executeQueryX(sql); + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceExit(this, "executeQuery", resultSet); + } + return resultSet; + } } - public int executeUpdate (String sql, String columnNames[]) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "executeUpdate", sql, columnNames); - generatedKeysColumnNames_ = columnNames; - int updateValue = executeUpdateX (sql); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "executeUpdate", updateValue); - return updateValue; - } + private ResultSet executeQueryX(String sql) throws SqlException { + flowExecute(executeQueryMethod__, sql); + + checkExecuteQueryPostConditions("java.sql.Statement"); + return resultSet_; } - public boolean execute (String sql, int autoGeneratedKeys) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "execute", sql, autoGeneratedKeys); - autoGeneratedKeys_ = autoGeneratedKeys; - boolean b = executeX (sql); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "execute", b); - return b; - } + void checkExecuteQueryPostConditions(String jdbcStatementInterfaceName) throws SqlException { + // We'll just rely on finalizers to close the dangling result sets. + if (resultSetList_ != null && resultSetList_.length != 1) { + throw new SqlException(agent_.logWriter_, jdbcStatementInterfaceName + ".executeQuery() cannot be called " + + "because multiple result sets were returned." + + " Use " + jdbcStatementInterfaceName + ".execute() to obtain multiple results."); + } + + if (resultSet_ == null) { + throw new SqlException(agent_.logWriter_, jdbcStatementInterfaceName + ".executeQuery() was called " + + "but no result set was returned." + + " Use " + jdbcStatementInterfaceName + ".executeUpdate() for non-queries."); + } } - public boolean execute (String sql, int columnIndexes[]) throws SqlException - { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "execute", sql, columnIndexes); - checkForClosedStatement (); - throw new SqlException (agent_.logWriter_, "Driver not capable"); + public int executeUpdate(String sql) throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "executeUpdate", sql); + } + int updateValue = executeUpdateX(sql); + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceExit(this, "executeUpdate", updateValue); + } + return updateValue; + } } - public boolean execute (String sql, String columnNames[]) throws SqlException - { - synchronized (connection_) { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "execute", sql, columnNames); - generatedKeysColumnNames_ = columnNames; - boolean b = executeX (sql); - if (agent_.loggingEnabled()) agent_.logWriter_.traceExit (this, "execute", b); - return b; - } + private int executeUpdateX(String sql) throws SqlException { + flowExecute(executeUpdateMethod__, sql); + + checkExecuteUpdatePostConditions("java.sql.Statement"); + return updateCount_; } - public int getResultSetHoldability () throws SqlException - { - if (agent_.loggingEnabled()) agent_.logWriter_.traceEntry (this, "getResultSetHoldability"); - checkForClosedStatement (); - return resultSetHoldability_; - } - - // ----------------------- box car and callback methods --------------------- - // All callbacks must be client-side only operations. - // Use of MaterialStatement interface is necessary to avoid multiple inheritance problem in Java. - public void writeSetSpecialRegister (java.util.ArrayList sqlsttList) throws SqlException - { materialStatement_.writeSetSpecialRegister_ (sqlsttList); } - - public void readSetSpecialRegister () throws SqlException - { materialStatement_.readSetSpecialRegister_(); } - - public void writeExecuteImmediate (String sql, - Section section) throws SqlException - { materialStatement_.writeExecuteImmediate_ (sql, section); } - public void readExecuteImmediate () throws SqlException - { materialStatement_.readExecuteImmediate_(); } - public void completeExecuteImmediate (Sqlca sqlca) - { - int sqlcode = completeSqlca (sqlca); - if (sqlcode < 0) return; - if (sqlca != null) - updateCount_ = sqlca.getUpdateCount(); - } - public void readExecuteImmediateForBatch (String sql) throws SqlException - { materialStatement_.readExecuteImmediateForBatch_ (sql); } - - public void writePrepareDescribeOutput (String sql, - Section section) throws SqlException - { materialStatement_.writePrepareDescribeOutput_ (sql, section); } - public void readPrepareDescribeOutput () throws SqlException - { materialStatement_.readPrepareDescribeOutput_(); } - - public void completePrepareDescribeOutput (ColumnMetaData resultSetMetaData, - Sqlca sqlca) - { - completePrepare (sqlca); - resultSetMetaData_ = resultSetMetaData; - if (agent_.loggingEnabled()) agent_.logWriter_.traceResultSetMetaData (this, resultSetMetaData_); - } - - // Used for re-prepares across commit only - public void writePrepare (String sql, Section section) throws SqlException - { materialStatement_.writePrepare_ (sql, section); } - public void readPrepare () throws SqlException - { materialStatement_.readPrepare_(); } - public void completePrepare (Sqlca sqlca) - { - int sqlcode = completeSqlca (sqlca); - if (sqlcode < 0) return; - markPrepared(); - } - - public void writeOpenQuery (Section section, - int fetchSize, - int resultSetType - ) throws SqlException - { - materialStatement_.writeOpenQuery_ (section, - fetchSize, - resultSetType); - } - public void readOpenQuery () throws SqlException - { materialStatement_.readOpenQuery_(); } - - public void completeOpenQuery (Sqlca sqlca, ResultSet resultSet) - { - completeSqlca (sqlca); - resultSet_ = resultSet; - // For NET, resultSet_ == null when open query fails and receives OPNQFLRM. - // Then, in NetStatementReply.parseOpenQueryFailure(), completeOpenQuery() is - // invoked with resultSet explicitly set to null. - if (resultSet == null) return; - resultSet.resultSetMetaData_ = resultSetMetaData_; - resultSet.resultSetMetaData_.resultSetConcurrency_ = resultSet.resultSetConcurrency_; - - // only cache the Cursor object for a PreparedStatement and if a Cursor object is - // not already cached. - if (cachedCursor_ == null && isPreparedStatement_) - cachedCursor_ = resultSet_.cursor_; - - // The following two assignments should have already happened via prepareEvent(), - // but are included here for safety for the time being. - if (sqlca != null && sqlca.getSqlCode() < 0) return; - openOnServer_ = true; - resultSet.cursor_.rowsRead_ = 0; - - // Set fetchSize_ to the default(64) if not set by the user if the resultset is scrollable. - // This fetchSize_ is used to check for a complete rowset when rowsets are parsed. - // For scrollable cursors when the fetchSize_ is not set, (fetchSize_ == 0), a default - // fetchSize of 64 is sent on behalf of the application, so we need to update the fetchSize_ - // here to 64. - if (resultSet_.fetchSize_ == 0 && - (resultSet_.resultSetType_ == java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE || - resultSet_.resultSetType_ == java.sql.ResultSet.TYPE_SCROLL_SENSITIVE)) - resultSet_.fetchSize_ = org.apache.derby.client.am.Configuration.defaultFetchSize; - } - - public void completeExecuteCallOpenQuery (Sqlca sqlca, ResultSet resultSet, ColumnMetaData resultSetMetaData, Section generatedSection) - { - resultSet.completeSqlca (sqlca); - // For CallableStatements we can't just clobber the resultSet_ here, must use setResultSetEvent() separately - resultSet.resultSetMetaData_ = resultSetMetaData; - - // The following two assignments should have already happened via prepareEvent(), - // but are included here for safety for the time being. - if (sqlca != null && sqlca.getSqlCode() < 0) return; - openOnServer_ = true; - resultSet.cursor_.rowsRead_ = 0; - - resultSet.generatedSection_ = generatedSection; - - // We are always sending the default fetchSize of 64 if not set for stored procedure calls. - // This is different from the "normal" cursor case for forward_only cursors, where if - // fetchSize_ is not set, we do not send any default value. Here since we always send - // the fetchSize_, we need to set it to what we sent. - if (resultSet.fetchSize_ == 0) - resultSet.fetchSize_ = org.apache.derby.client.am.Configuration.defaultFetchSize; - } - - public void writeExecuteCall (boolean outputExpected, - String procedureName, - Section section, - int fetchSize, - boolean suppressResultSets, // for batch updates == true - int resultSetType, - ColumnMetaData parameterMetaData, - Object[] inputs) throws SqlException - { - materialStatement_.writeExecuteCall_ (outputExpected, - procedureName, - section, - fetchSize, - suppressResultSets, - resultSetType, - parameterMetaData, - inputs); - } - public void readExecuteCall () throws SqlException - { materialStatement_.readExecuteCall_(); } - public void completeExecuteCall (Sqlca sqlca, Cursor singletonParams, ResultSet[] resultSets) - { - completeExecuteCall (sqlca, singletonParams); - resultSetList_ = resultSets; - if (resultSets != null) - resultSet_ = resultSets[0]; - indexOfCurrentResultSet_ = 0; - } - public void completeExecuteCall (Sqlca sqlca, Cursor singletonParams) // no result sets returned - { - completeExecute (sqlca); - //if ((sqlca != null) && ((sqlca.getSqlCode() < 0) || (sqlca.getSqlCode() == 100))) - if (sqlca != null && sqlca.getSqlCode() < 0) - singletonRowData_ = null; - else { - singletonRowData_ = singletonParams; - if (cachedSingletonRowData_ == null && isPreparedStatement_) - cachedSingletonRowData_ = singletonRowData_; - } - } - // Callback for CALLS, and PreparedStatement updates. - public void completeExecute (Sqlca sqlca) - { - if (sqlca == null) return; - - int sqlcode = sqlca.getSqlCode(); - if (sqlcode < 0) { - agent_.accumulateReadException (new SqlException (agent_.logWriter_, sqlca)); - returnValueFromProcedure_ = sqlcode; - } - else { - updateCount_ = sqlca.getUpdateCount(); - // sometime for call statement, protocol will return updateCount_, we will always set that to 0 - // sqlMode_ is not set for statements, only for prepared statements - if (sqlMode_ == isCall__) { - updateCount_ = -1; - returnValueFromProcedure_ = sqlca.getSqlErrd()[0]; ////what is this for?? + void checkExecuteUpdatePostConditions(String jdbcStatementInterfaceName) throws SqlException { + // We'll just rely on finalizers to close the dangling result sets. + if (resultSetList_ != null) { + throw new SqlException(agent_.logWriter_, jdbcStatementInterfaceName + ".executeUpdate() cannot be called " + + "because multiple result sets returned." + + " Use " + jdbcStatementInterfaceName + ".execute() to obtain multiple results."); } - // Sqlcode 466 indicates a call statement has issued and result sets returned. - // This is a good place to set some state variable to indicate result sets are open - // for call, so that when autocommit is true, commit will not be issued until the - // result sets are closed. - // Currently, commit is not issued even there is no result set. - // do not externalize sqlcode +100 - if (sqlcode > 0 && sqlcode != 466 && sqlcode != 100) - accumulateWarning (new SqlWarning (agent_.logWriter_, sqlca)); - } - } - - - public void setUpdateCount (int updateCount) { updateCount_ = updateCount; } - - - private boolean willTickleServer (int number, boolean allowAutoCommits) throws SqlException - { - boolean requiresAutocommit = false; - if (resultSetList_ != null) { - for (int i = 0; i < number; i++) { - if (resultSetList_[i] != null) { - if (resultSetList_[i].openOnServer_) - return true; // for the writeClose flow - if (!resultSetList_[i].autoCommitted_ && allowAutoCommits) - requiresAutocommit = true; // for the commit flow - } - } - } - else if (generatedKeysResultSet_ != null && generatedKeysResultSet_.openOnServer_) { - generatedKeysResultSet_.writeClose (); - } - else if (resultSet_ != null) { - if (resultSet_.openOnServer_) - return true; // for the writeClose flow - if (!resultSet_.autoCommitted_ && allowAutoCommits) - requiresAutocommit = true; - } - if (connection_.autoCommit_ && requiresAutocommit) { // for the auto-commit; - if (connection_.isXAConnection_) - return (connection_.xaState_ == Connection.XA_LOCAL) || (connection_.xaState_ == Connection.XA_LOCAL_START_SENT); - else - return true; - } - return false; - } - - private void flowClose () throws SqlException - { - agent_.beginWriteChain (this); - writeClose (true); // true means permit auto-commits - agent_.flow (this); - readClose (true); // true means permit auto-commits - agent_.endReadChain(); - } - - private void flowCloseOutsideUOW () throws SqlException - { - agent_.beginWriteChainOutsideUOW (); - writeClose (true); // true means permit auto-commits - agent_.flowOutsideUOW (); - readClose (true); // true means permit auto-commits - agent_.endReadChain(); - } - - final void writeClose (boolean allowAutoCommits) throws SqlException - { - writeCloseResultSets (allowAutoCommits); - } - - final void readClose (boolean allowAutoCommits) throws SqlException - { - readCloseResultSets (allowAutoCommits); - } - - boolean writeCloseResultSets (boolean allowAutoCommits) throws SqlException - { - int numberOfResultSetsToClose = (resultSetList_ == null) ? 0 : resultSetList_.length; - return writeCloseResultSets (numberOfResultSetsToClose, allowAutoCommits); - } - - // The connection close processing passes allowAutoCommits=false because if we drove an - // autocommits after each statement close, then when we issue close requests on non-held cursors - // the server would complain that the non-held cursor was already closed from the previous statement's auto-commit. - // So the solution is to never autocommit statements during connection close processing. - // - // Here's the operative explanation: - // Given a sequence of open statements S1, S2, .... a logic problem is occuring after S1 close-query - // drives an auto-commit, and S2 close-query is driven against a non-held cursor. - // The first auto-commit driven by S1 triggers a callback that closes S2's non-held cursor, - // and so the subsequent S2 close-query request generates an error from the server saying - // that the cursor is already closed. - // - // This is fixed by passing a flag to our statement close processing that prevents - // driving additional auto-commits after each statement close. - // Connectino close drives its own final auto-commit. - // - boolean writeCloseResultSets (int number, boolean allowAutoCommits) throws SqlException - { - boolean requiresAutocommit = false; - if (resultSetList_ != null) { - for (int i = 0; i < number; i++) { - if (resultSetList_[i] != null) { - if (resultSetList_[i].openOnServer_) - resultSetList_[i].writeClose (); - if (!resultSetList_[i].autoCommitted_ && allowAutoCommits) - requiresAutocommit = true; - } - } - } - else if (generatedKeysResultSet_ != null && generatedKeysResultSet_.openOnServer_) { - generatedKeysResultSet_.writeClose (); - } - else if (resultSet_ != null) { - if (resultSet_.openOnServer_) - resultSet_.writeClose (); - if (!resultSet_.autoCommitted_ && allowAutoCommits) - requiresAutocommit = true; - } - if (connection_.autoCommit_ && requiresAutocommit && isAutoCommittableStatement_) { - connection_.writeAutoCommit (); - if (connection_.isXAConnection_) - return (connection_.xaState_ == Connection.XA_LOCAL) || (connection_.xaState_ == Connection.XA_LOCAL_START_SENT); - else - return true; - } - return false; - } - - // Helper method for S.flowCloseResultSets() and PS.flowExecute() - void readCloseResultSets (boolean allowAutoCommits) throws SqlException - { - int numberOfResultSetsToClose = (resultSetList_ == null) ? 0 : resultSetList_.length; - readCloseResultSets (numberOfResultSetsToClose, allowAutoCommits); - } - - void readCloseResultSets (int number, boolean allowAutoCommits) throws SqlException - { - boolean requiredAutocommit = false; - if (resultSetList_ != null) { - for (int i = 0; i < number; i++) { - if (resultSetList_[i] != null) { - if (resultSetList_[i].openOnServer_) - resultSetList_[i].readClose (); - else - resultSetList_[i].markClosed(); - if (!resultSetList_[i].autoCommitted_ && allowAutoCommits) - requiredAutocommit = true; - } - } - } - else if (generatedKeysResultSet_ != null) { - if (generatedKeysResultSet_.openOnServer_) - generatedKeysResultSet_.readClose (); - else - generatedKeysResultSet_.markClosed (); - } - else if (resultSet_ != null) { - if (resultSet_.openOnServer_) - resultSet_.readClose (); - else - resultSet_.markClosed(); - if (!resultSet_.autoCommitted_ && allowAutoCommits) - requiredAutocommit = true; - } - // we only commit when auto commit is turned on and at least one result set needed closing on server. - if (connection_.autoCommit_ && requiredAutocommit && isAutoCommittableStatement_) { - connection_.readAutoCommit (); - } - } - - private void flowCloseRetrievedResultSets () throws SqlException - { - int numberOfResultSetsToClose = (resultSetList_ == null) ? 0 : indexOfCurrentResultSet_+1; - agent_.beginWriteChain (this); - // Need to refactor the ResultSet.readClose() path to check if we are the - // last result set closed in a set of multiple result sets of the owning statement, - // if so, we need to flow the auto-commit (but only then). - // currently, the code to do this is only in the closeX() path, which isn't called here - writeCloseResultSets (numberOfResultSetsToClose, false); - agent_.flow (this); - readCloseResultSets (numberOfResultSetsToClose, false); // true means permit auto-commits - agent_.endReadChain(); - } - - private void flowCloseRetrievedResultSetsOutsideUOW () throws SqlException - { - int numberOfResultSetsToClose = (resultSetList_ == null) ? 0 : indexOfCurrentResultSet_+1; - agent_.beginWriteChainOutsideUOW (); - // Need to refactor the ResultSet.readClose() path to check if we are the - // last result set closed in a set of multiple result sets of the owning statement, - // if so, we need to flow the auto-commit (but only then). - // currently, the code to do this is only in the closeX() path, which isn't called here - writeCloseResultSets (numberOfResultSetsToClose, false); - agent_.flowOutsideUOW (); - readCloseResultSets (numberOfResultSetsToClose, false); // true means permit auto-commits - agent_.endReadChain(); - } - - public int completeSqlca (Sqlca sqlca) - { - if (sqlca == null) return 0; - int sqlcode = sqlca.getSqlCode(); - if (sqlcode < 0) - connection_.agent_.accumulateReadException (new SqlException (agent_.logWriter_, sqlca)); - else if (sqlcode > 0) - accumulateWarning (new SqlWarning (agent_.logWriter_, sqlca)); - return sqlcode; - } - - public void completeExecuteSetStatement (Sqlca sqlca) - { - } - - void markClosedOnServer () - { - if (section_ != null) { - section_.free(); - section_ = null; - } - openOnServer_ = false; - // if an error occurs during the middle of the reset, before the statement - // has a chance to reset its materialStatement_, and Agent.disconnectEvent() is called, - // then the materialStatement_ here can be null. - if (materialStatement_ != null) - materialStatement_.markClosedOnServer_(); - } - - void markClosed () - { - openOnClient_ = false; - markResultSetsClosed(); - // in case a cursorName was set on the Statement but the Statement was - // never used to execute a query, the cursorName will not be removed - // when the resultSets are mark closed, so we need to remove the - // cursorName form the cache. - removeClientCursorNameFromCache (); - markPreparedStatementForAutoGeneratedKeysClosed (); - markClosedOnServer(); - } - - void markPreparedStatementForAutoGeneratedKeysClosed () - { - if (preparedStatementForAutoGeneratedKeys_ != null) - preparedStatementForAutoGeneratedKeys_.markClosed(); - } - - void markResultSetsClosed() - { - if (resultSetList_ != null) { - for (int i = 0; i < resultSetList_.length; i++) { - if (resultSetList_[i] != null) resultSetList_[i].markClosed(); - resultSetList_[i] = null; - } - } - if (generatedKeysResultSet_ != null) generatedKeysResultSet_.markClosed(); - if (resultSet_ != null) resultSet_.markClosed(); - resultSet_ = null; - resultSetList_ = null; - generatedKeysResultSet_ = null; - } - - private void flowExecute (int executeType, String sql) throws SqlException - { - checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) - checkAutoGeneratedKeysParameters(); - clearWarningsX(); // Per jdbc spec 0.7, and getWarnings() javadoc - - sql = escape (sql); - parseSqlAndSetSqlModes (sql); - if (sqlMode_ == isUpdate__) updateCount_ = 0; - else updateCount_ = -1; - - checkForAppropriateSqlMode (executeType, sqlMode_); - - java.util.Timer queryTimer = null; - QueryTimerTask queryTimerTask = null; - if (timeout_ != 0) { - queryTimer = new java.util.Timer (); // A thread that ticks the seconds - queryTimerTask = new QueryTimerTask (this, queryTimer); - queryTimer.schedule (queryTimerTask, 1000*timeout_); - } - - // enclose the processing in a try finally block in order to make sure - // the query timeout is cancelled at the end of this method. - try { - agent_.beginWriteChain (this); - boolean piggybackedAutoCommit = writeCloseResultSets (true); // true means permit auto-commits - - ResultSet scrollableRS = null; - Section newSection = null; - boolean repositionedCursor = false; - - switch (sqlMode_) { - case isQuery__: - newSection = agent_.sectionManager_.getDynamicSection ( - resultSetHoldability_); - // if client's cursor name is set, map it to the query section in the hashtable - // after we obtain the section. - if (cursorName_ != null) { - agent_.sectionManager_.mapCursorNameToQuerySection (cursorName_, newSection); + // We'll just rely on the finalizer to close the dangling result set. + if (resultSet_ != null) { + throw new SqlException(agent_.logWriter_, jdbcStatementInterfaceName + ".executeUpdate() was called " + + "but a result set was returned." + + " Use " + jdbcStatementInterfaceName + ".executeQuery() to obtain a result set."); + } + } + + // The server holds statement resources until transaction end. + public void close() throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "close"); + } + closeX(); + } + } - // This means we must subtitute the <users-cursor-name> with the <canned-cursor-name> - // in the pass-thru sql string "...where current of <canned-cursor-name>". - newSection.setClientCursorName (cursorName_); + // An untraced version of close() + public void closeX() throws SqlException { + if (!openOnClient_) { + return; + } + // Regardless of whether or not this statement is in the prepared state, + // we need to close any open cursors for this statement on the server. + int numberOfResultSetsToClose = (resultSetList_ == null) ? 0 : resultSetList_.length; + boolean willTickleServer = willTickleServer(numberOfResultSetsToClose, true); + try { + if (willTickleServer) { + flowClose(); + } else { + flowCloseOutsideUOW(); + } + } finally { + markClosed(); + connection_.openStatements_.remove(this); + } + // push the mark close of rsmd into Statement.markClosed() method + if (resultSetMetaData_ != null) { + resultSetMetaData_.markClosed(); + resultSetMetaData_ = null; } + } - writePrepareDescribeOutput (sql, newSection); - writeOpenQuery (newSection, - fetchSize_, - resultSetType_); - break; - case isUpdate__: - String cursorName = null; - if(sqlUpdateMode_ == isDeleteSql__ || sqlUpdateMode_ == isUpdateSql__) { - String[] sqlAndCursorName = extractCursorNameFromWhereCurrentOf (sql); - if(sqlAndCursorName != null) { - cursorName = sqlAndCursorName[0]; - sql = sqlAndCursorName[1]; - } - } - if (cursorName != null) { - newSection = agent_.sectionManager_.getPositionedUpdateSection (cursorName, true); // true means get an execute immediate section - if (newSection == null) - throw new SqlException (agent_.logWriter_, "Invalid cursor name \"" + cursorName + - "\" in the Update/Delete statement."); - scrollableRS = agent_.sectionManager_.getPositionedUpdateResultSet (cursorName); - // do not need to reposition for rowset cursors - if (scrollableRS != null && !scrollableRS.isRowsetCursor_) { - repositionedCursor = - scrollableRS.repositionScrollableResultSetBeforeJDBC1PositionedUpdateDelete(); - if (!repositionedCursor) scrollableRS = null; - } - - // if client's cursor name is set, and the cursor name in the positioned update - // string is the same as the client's cursor name, replace client's cursor name - // with the server's cursor name. - if (newSection.getClientCursorName() != null && - cursorName.compareTo (newSection.getClientCursorName()) == 0) { - // substitute cusor name in pass thru sql string - sql = substituteClientCursorNameWithServerCursorName (sql, newSection); - } - writeExecuteImmediate (sql, newSection); - } - // if sql is an insert and columnNames is not null, and - // then transform the insert statement into an - // select from insert statement. - // else chain an "select from identity_val_local()" to the insert statement - else if (sqlUpdateMode_ == isInsertSql__ && generatedKeysColumnNames_ != null) { - newSection = agent_.sectionManager_.getDynamicSection ( - resultSetHoldability_); - writePrepareDescribeOutput (constructSelectFromInsertSQL(sql), newSection); - writeOpenQuery (newSection, - fetchSize_, - resultSetType_); - } - else { - newSection = agent_.sectionManager_.getDynamicSection(resultSetHoldability_); - - writeExecuteImmediate (sql, newSection); - if (sqlUpdateMode_ == isInsertSql__ && autoGeneratedKeys_ == RETURN_GENERATED_KEYS) { - prepareAutoGeneratedKeysStatement (); - writeOpenQuery (preparedStatementForAutoGeneratedKeys_.section_, - preparedStatementForAutoGeneratedKeys_.fetchSize_, - preparedStatementForAutoGeneratedKeys_.resultSetType_); - } - } - - // maybe duplicate a commit here if the sql is a "commit" - if (connection_.autoCommit_) connection_.writeAutoCommit (); - break; - case isCall__: - newSection = writeExecuteCall (sql, false); - - break; - } - - agent_.flow (this); - - readCloseResultSets (true); // true means permit auto-commits - - // turn inUnitOfWork_ flag back on and add statement - // back on commitListeners_ list if they were off - // by an autocommit chained to a close cursor. - if (piggybackedAutoCommit) { - connection_.completeTransactionStart(); - } - - markResultSetsClosed(); - markClosedOnServer(); - section_ = newSection; - - switch (sqlMode_) { - case isQuery__: - // parse out the reply to a chained prepare and open request - readPrepareDescribeOutput (); - // This establishes statement.resultSet - readOpenQuery (); - - // resultSet_ is null if open query failed. - // check for null resultSet_ before using it. - // the first rowset comes back on OPEN for static non-rowset cursors. - // no row is returned on open for rowset cursors. - if (resultSet_ != null) { - resultSet_.parseScrollableRowset (); - // If client's cursor name is set, map the client's cursor name to the ResultSet - // Else map the server's cursor name to the ResultSet - mapCursorNameToResultSet(); + public int getMaxFieldSize() throws SqlException { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "getMaxFieldSize"); + } + checkForClosedStatement(); + return maxFieldSize_; + } + + public void setMaxFieldSize(int max) throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "setMaxFieldSize", max); + } + checkForClosedStatement(); + if (max < 0) { + throw new SqlException(agent_.logWriter_, "Invalid maxFieldSize value: " + max); + } + maxFieldSize_ = max; } + } + + public int getMaxRows() throws SqlException { + checkForClosedStatement(); + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceExit(this, "getMaxRows", maxRows_); + } + return maxRows_; + } + + public void setMaxRows(int maxRows) throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "setMaxRows", maxRows); + } + checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) + if (maxRows < 0) { + throw new SqlException(agent_.logWriter_, "Invalid maxRows value: " + maxRows); + } + maxRows_ = maxRows; + } + } + + public void setEscapeProcessing(boolean enable) throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "setEscapeProcessing", enable); + } + checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) + } + } + + public int getQueryTimeout() throws SqlException { + checkForClosedStatement(); + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceExit(this, "getQueryTimeout", timeout_); + } + return timeout_; + } + + public void setQueryTimeout(int seconds) throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "setQueryTimeout", seconds); + } + checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) + if (seconds < 0) { + throw new SqlException(agent_.logWriter_, "Attempt to set a negative query timeout"); + } + timeout_ = seconds; // java.util.Timer takes milliseconds + } + } + + public void cancel() throws SqlException { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "cancel"); + } + checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) + throw new SqlException(agent_.logWriter_, "cancel() not supported by server"); + } + + public java.sql.SQLWarning getWarnings() throws SqlException { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceExit(this, "getWarnings", warnings_); + } + return warnings_; + } + + public void clearWarnings() throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "clearWarnings"); + } + clearWarningsX(); + } + } + + // An untraced version of clearWarnings() + public void clearWarningsX() { + warnings_ = null; + } + + // Dnc statements are already associated with a unique cursor name as defined + // by our canned dnc package set. + // ResultSet.getCursorName() should be used to + // obtain the for update cursor name to use when executing a positioned update statement. + // See Jdbc 3 spec section 14.2.4.4. + public void setCursorName(String name) throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "setCursorName", name); + } + checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) + if (name == null || name.equals("")) { + throw new SqlException(agent_.logWriter_, "Invalid cursor name."); + } + + // Invalid to set the cursor name if there are ResultSet's open on the Statement. + if (resultSet_ != null && resultSet_.openOnClient_) { + throw new SqlException(agent_.logWriter_, "Invalid operation: setCursorName() " + + "called when there are open ResultSet's on the Statement."); + } + + // Duplicate cursor names not allowed. + if (connection_.clientCursorNameCache_.containsKey(name)) { + throw new SqlException(agent_.logWriter_, "Duplicate cursor names are not allowed."); + } + connection_.clientCursorNameCache_.put(name, name); + + // section_ is null for Statement objects. We will defer the mapping of cursorName + // to section until when the query is executed. + if (section_ != null) { + agent_.sectionManager_.mapCursorNameToQuerySection(name, (Section) section_); + + // This means we must subtitute the <users-cursor-name> with the <canned-cursor-name> + // in the pass-thru sql string "...where current of <canned-cursor-name>". + section_.setClientCursorName(name); + } + cursorName_ = name; + } + } + + //----------------------- Multiple Results -------------------------- - break; - case isUpdate__: + public boolean execute(String sql) throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "execute", sql); + } + boolean b = executeX(sql); + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceExit(this, "execute", b); + } + return b; + } + } - // do not need to reposition for rowset cursors. - if (scrollableRS != null && !scrollableRS.isRowsetCursor_) - scrollableRS.readPositioningFetch_(); + boolean executeX(String sql) throws SqlException { + flowExecute(executeMethod__, sql); + return resultSet_ != null; + } + + public java.sql.ResultSet getResultSet() throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "getResultSet"); + } + checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceExit(this, "getResultSet", resultSet_); + } + return resultSet_; + } + } + + public int getUpdateCount() throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "getUpdateCount"); + } + checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceExit(this, "getUpdateCount", updateCount_); + } + return updateCount_; + } + } - if (sqlUpdateMode_ == isInsertSql__ && generatedKeysColumnNames_ != null) { - readPrepareDescribeOutput (); - readOpenQuery (); - if (resultSet_ != null) { - generatedKeysResultSet_ = resultSet_; + public boolean getMoreResults() throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "getMoreResults"); + } + boolean resultIsResultSet = getMoreResultsX(CLOSE_ALL_RESULTS); + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceExit(this, "getMoreResults", resultIsResultSet); + } + return resultIsResultSet; + } + } + + //--------------------------JDBC 2.0----------------------------- + + public void setFetchDirection(int direction) throws SqlException { + synchronized (connection_) { + if (agent_.loggingEnabled()) { + agent_.logWriter_.traceEntry(this, "setFetchDirection", direction); + } + checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc) + switch (direction) { + case java.sql.ResultSet.FETCH_FORWARD: + case java.sql.ResultSet.FETCH_REVERSE: + case java.sql.ResultSet.FETCH_UNKNOWN: + fetchDirection_ = direction; + break; + default: + throw new SqlException(agent_.logWriter_, "Invalid fetch direction " + direction); + } + } + } +
[... 1906 lines stripped ...]