Added: 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=165178&view=auto ============================================================================== --- incubator/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java (added) +++ incubator/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java Thu Apr 28 12:05:42 2005 @@ -0,0 +1,1975 @@ +/* + + Derby - Class org.apache.derby.client.am.Statement + + Copyright (c) 2001, 2005 The Apache Software Foundation or its licensors, where applicable. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +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(); + resultSet_ = null; + } + resultIsResultSet = false; + } + 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 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_; + } + + 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; + } + } + + 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"); + } + + 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; + } + } + + 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; + } + } + + 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 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; + } + } + + 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?? + } + // 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); + + // 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_); + } + + 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(); + } + + break; + + case isUpdate__: + + // do not need to reposition for rowset cursors. + if (scrollableRS != null && !scrollableRS.isRowsetCursor_) + scrollableRS.readPositioningFetch_(); + + if (sqlUpdateMode_ == isInsertSql__ && generatedKeysColumnNames_ != null) { + readPrepareDescribeOutput (); + readOpenQuery (); + if (resultSet_ != null) { + generatedKeysResultSet_ = resultSet_; + resultSet_ = null; + updateCount_ = 1; + } + } + else { + readExecuteImmediate (); + + if (sqlUpdateMode_ == isInsertSql__ && autoGeneratedKeys_ == RETURN_GENERATED_KEYS) { + readPrepareAutoGeneratedKeysStatement (); + preparedStatementForAutoGeneratedKeys_.readOpenQuery (); + generatedKeysResultSet_ = preparedStatementForAutoGeneratedKeys_.resultSet_; + preparedStatementForAutoGeneratedKeys_.resultSet_ = null; + } + } + + if (connection_.autoCommit_) connection_.readAutoCommit (); + break; + + case isCall__: + readPrepare(); + readExecuteCall(); + break; + + } + + // in the case the stored procedure call is uncatalogued, we need to catch that + // kind exception and changed the call from dynamic to static + agent_.endReadChain(); + + // If we hear from Sun that we can just set a warning for this, then move this code to the ResultSet constructor. + // Throw an exception if holdability returned by the server is different from requested. + if (resultSet_ != null && resultSet_.resultSetHoldability_ != resultSetHoldability_ && sqlMode_ != isCall__) + throw new SqlException (agent_.logWriter_, "Unable to open resultSet with requested " + + "holdability " + resultSetHoldability_ + "."); + } + finally { + // We don't want to cancel immediately after flow since there is still incoming data on the wire and + // so logically we have not completed a full traversal from the client to the server and back. + // Cancelling the query timers needs to occur after endReadChain() in order to avoid deadlock conditions. + if (timeout_ != 0) { // query timers need to be cancelled. + queryTimer.cancel(); + queryTimerTask.cancel(); + } + } + + // In the case of executing a call to a stored procedure. + if (sqlMode_ == isCall__) { + parseStorProcReturnedScrollableRowset (); + // When there is no result sets back, we will commit immediately when autocommit is true. + if (connection_.autoCommit_ && resultSet_ == null && resultSetList_ == null) + connection_.flowAutoCommit(); + } + } + + void flowExecuteBatch (int[] updateCounts) throws SqlException, BatchUpdateException + { + SqlException chainBreaker = null; + boolean isCallCataloguedBestGuess = true; + agent_.beginBatchedWriteChain (this); + for (int i = 0; i < batch_.size(); i++) { + boolean flowSQL = true; + String sql = (String) batch_.get (i); + parseSqlAndSetSqlModes (sql); + try { checkForInvalidBatchedSql (sql); } catch (SqlException e) { flowSQL = false; } + + // if you have a length mismatch for a lob flow, then we need to return a -3 + // need to trap the exceptions coming back from writeExecuteImmediate and continue on with a -3 + // net will need to be able to reset the send buffer + if (flowSQL) { + if (section_ != null) section_.free(); + if (sqlMode_ != isCall__) { + section_ = + agent_.sectionManager_.getDynamicSection(resultSetHoldability_); + writeExecuteImmediate(sql, section_); + } + else { + section_ = writeExecuteCall(sql, true); + } + } + } + + if (connection_.autoCommit_) connection_.writeAutoCommit (); + + agent_.flowBatch (this, batch_.size()); + + try { + for (int i = 0; i < batch_.size(); i++) { + agent_.setBatchedExceptionLabelIndex (i); + SqlException invalidSQLCaughtByClient = null; + String sql = (String) batch_.get (i); + parseSqlAndSetSqlModes (sql); + try { checkForInvalidBatchedSql (sql); } catch (SqlException e) { invalidSQLCaughtByClient = e; } + if (invalidSQLCaughtByClient == null) { + updateCount_ = -1; + if(sqlMode_ != isCall__){ + readExecuteImmediateForBatch (sql); + } + else { + if (isCallCataloguedBestGuess) + readPrepare(); + readExecuteCall (); + } + } + else { + agent_.accumulateReadException (invalidSQLCaughtByClient); + updateCount_ = java.sql.Statement.EXECUTE_FAILED; + invalidSQLCaughtByClient = null; + } + + updateCounts[i] = updateCount_; + + // DERBY doesn't return an update count for DDL statements, so we need to + // remap our initial value of -1 (represents invalid update count) to a + // valid update count of zero. + if (updateCounts [i] == -1) { + updateCounts[i] = 0; + } + } + agent_.disableBatchedExceptionTracking(); // to prvent the following readCommit() from getting a batch label + if (connection_.autoCommit_) connection_.readAutoCommit(); // this could throw a chainbreaker too + } + // for chain-breaking exception only, all read() methods do their own accumulation + // this catches the entire accumulated chain, we need to be careful not to + // reaccumulate it on the agent since the batch labels will be overwritten if + // batch exception tracking is enabled. + catch (SqlException e) { + chainBreaker = e; + chainBreaker.setNextException (new SqlException (agent_.logWriter_, + "Non-recoverable chain-breaking exception occurred during batch processing. " + + "The batch is terminated non-atomically.")); + } + // We need to clear the batch before any exception is thrown from agent_.endBatchedReadChain(). + batch_.clear (); + agent_.endBatchedReadChain (updateCounts, chainBreaker); + } + + private Section writeExecuteCall (String sql, + boolean isBatch) throws SqlException + { + Section newSection = null; + + newSection = agent_.sectionManager_.getDynamicSection ( + resultSetHoldability_); + // this code is beneficial only if there is literal in the sql call statement + writePrepare (sql, newSection); + writeExecuteCall (false, // no out parameters, outputExpected = false + null, // sql is prepared, procedureName = null + newSection, + fetchSize_, + isBatch, // do not suppress ResultSets for regular CALLs + resultSetType_, + null, // no parameters, parameterMetaData = null + null); // no parameters, inputs = null + + return newSection; + } + + //------------------material layer event callbacks follow--------------------- + // All callbacks are client-side only operations + + public void listenToUnitOfWork () {} // do nothing for now. + public void completeLocalCommit (java.util.Iterator listenerIterator) {} // do nothing for now. + public void completeLocalRollback (java.util.Iterator listenerIterator) {} // do nothing for now. + + // This method will not work if e is chained. + // It is assumed that e is a single warning and is not chained. + public void accumulateWarning (SqlWarning e) + { + if (warnings_ == null) + warnings_ = e; + else + warnings_.setNextException (e); + } + + private void markPrepared() + { + //openOnClient_ = true; + openOnServer_ = true; + listenToUnitOfWork(); + } + + //-------------------------------helper methods------------------------------- + + // Should investigate if it can be optimized.. if we can avoid this parsing.. + // + void parseSqlAndSetSqlModes (String sql) throws SqlException + { + java.util.StringTokenizer tokenizer = new java.util.StringTokenizer (sql, "\t\n\r\f=? ("); + if (!tokenizer.hasMoreTokens()) throw new SqlException (agent_.logWriter_, "SQL passed with no tokens"); + + sqlUpdateMode_ = 0; + String firstToken = tokenizer.nextToken(); + + if (firstToken.equalsIgnoreCase ("select") || // captures <subselect> production + firstToken.equalsIgnoreCase ("values")) // captures <values-clause> production + sqlMode_ = isQuery__; + else if (firstToken.equalsIgnoreCase ("call")) // captures CALL...and ?=CALL... + sqlMode_ = isCall__; + else + parseUpdateSql (firstToken); + } + private void parseUpdateSql (String firstToken) throws SqlException + { + sqlMode_ = isUpdate__; + if (firstToken.equalsIgnoreCase("insert")) + sqlUpdateMode_ = isInsertSql__; + if (firstToken.equalsIgnoreCase("delete")) + sqlUpdateMode_ = isDeleteSql__; + if (firstToken.equalsIgnoreCase("update")) + sqlUpdateMode_ = isUpdateSql__; + } + + // the sql is assumed to start with CALL... or ?=CALL... + String getProcedureName (String sql) throws SqlException + { + java.util.StringTokenizer tokenizer = new java.util.StringTokenizer (sql, "\t\n\r\f= (?"); + if (!tokenizer.hasMoreTokens()) throw new SqlException (agent_.logWriter_, "bugcheck"); + String firstToken = tokenizer.nextToken(); + if (! firstToken.equalsIgnoreCase ("call")) throw new SqlException (agent_.logWriter_, "bugcheck"); + if (! tokenizer.hasMoreTokens()) throw new SqlException (agent_.logWriter_, "Invalid CALL syntax"); + return tokenizer.nextToken(); + } + + // Try to enforce the use of this method later. + public static String upperCaseProcedureName (String procedureName) throws SqlException + { + // upper case the parts of a 3-part procedure name unless the part is in a double quotes + + // Loop thru every character, if we're in double quotes just echo it, + // if we're not in double quotes, upper case it. + char [] charArray = null; + if (procedureName.indexOf("\"") == -1) + return procedureName.toUpperCase(); + else { + charArray = procedureName.toCharArray(); + boolean inStringLiteral = false; + for (int i = 0; i< charArray.length; i++) { + if (charArray[i] == '"') + inStringLiteral = !inStringLiteral; + else if (!inStringLiteral && charArray[i] != '.') + charArray[i] = Character.toUpperCase(charArray[i]); + } + } + return new String (charArray); + } + + void checkForAppropriateSqlMode (int executeType, int sqlMode) throws SqlException + { + if (executeType == executeQueryMethod__ && sqlMode == isUpdate__) + throw new SqlException (agent_.logWriter_, "executeQuery method cannot be used for update."); + if (executeType == executeUpdateMethod__ && sqlMode == isQuery__) + throw new SqlException (agent_.logWriter_, "executeUpdate method cannot be used for query."); + } + + void checkForClosedStatement () throws SqlException + { + // For some odd reason, there was a JVM hotspot error with Sun's 1.4 JDK + // when the code was written like this: + // agent_checkForDeferredExceptions(); + // if (!openOnClient_) + // throw new SqlException (agent_.logWriter_, "Invalid operation: statement closed"); + // + if (!openOnClient_) { + agent_.checkForDeferredExceptions(); + throw new SqlException (agent_.logWriter_, "Invalid operation: statement closed"); + } + else { + agent_.checkForDeferredExceptions(); + } + } + // precondition: parseSqlAndSetSqlModes() must be called on the supplied sql string before invoking this method + void checkForInvalidBatchedSql (String sql) throws SqlException + { + if (sql == null) throw new SqlException (agent_.logWriter_, "Null batched SQL string passed."); + + if (sqlMode_ != isCall__ + && !(sqlMode_ == isUpdate__ + && (sqlUpdateMode_ == isInsertSql__ + || sqlUpdateMode_ == isDeleteSql__ + || sqlUpdateMode_ == isUpdateSql__ + || sqlUpdateMode_ == 0)))// For any undefined pass thru statement like drop create + throw new SqlException(agent_.logWriter_, "Invalid SQL in Batch"); + } + + + String[] extractCursorNameFromWhereCurrentOf (String sql) + { + String lowerSql = sql.toLowerCase(); + int currentIndex = lowerSql.lastIndexOf("current"); + if(currentIndex != -1) { + int whereIndex = lowerSql.lastIndexOf("where"); + if(whereIndex != -1) { + String[] whereCurrentOf = {"where", "current", "of"}; + java.util.StringTokenizer st = new java.util.StringTokenizer (sql.substring(whereIndex)); + while (st.hasMoreTokens()) { + if (st.nextToken().equalsIgnoreCase (whereCurrentOf[0]) && + st.nextToken().equalsIgnoreCase (whereCurrentOf[1]) && + st.nextToken().equalsIgnoreCase (whereCurrentOf[2])) { + String cursorName = st.nextToken(); + String oldCursorName = cursorName; + int originalCursorNameLength = cursorName.length(); + int index = sql.lastIndexOf (cursorName); + if (cursorName.charAt(0) == '\"' && cursorName.charAt(cursorName.length()-1) == '\"') + cursorName = cursorName.substring(1, cursorName.length()-1); + else + cursorName = cursorName.toUpperCase(); + // we cannot assume "where current of cursorName" is always the end of the sql string + // with rowset cursors, it can be "where current of cursorName for row X of rowset" + if (sql.length() > index+originalCursorNameLength) + sql = sql.substring (0, index) + cursorName + sql.substring (index+oldCursorName.length(), sql.length()); + else + sql = sql.substring (0, index) + cursorName; + return new String[] {cursorName, sql}; // delimited name, so just extract the name. + } + } + } + } + return null; + } + + // Substitute the client cursor name in the SQL string with the server's cursor name. + // Only called on positioned update statements. + protected String substituteClientCursorNameWithServerCursorName (String sql, + Section section) throws SqlException + { + String clientCursorName = section.getClientCursorName(); + int index = sql.lastIndexOf (clientCursorName); + if (sql.length() > index+clientCursorName.length()) + return sql.substring (0, index) + section.getServerCursorNameForPositionedUpdate() + + sql.substring (index+clientCursorName.length(), sql.length()); + else + return sql.substring (0, index) + section.getServerCursorNameForPositionedUpdate(); + } + + public ConnectionCallbackInterface getConnectionCallbackInterface () + { + return connection_; + } + + // Only called on positioned upate statements + void resetCursorNameAndRemoveFromWhereCurrentOfMappings () + { + // Remove client/server cursorName -> ResultSet mapping from the hashtable. + // If Statement.close() is called before ResultSet.close(), then statement_.section is null. + if (section_ != null) { + agent_.sectionManager_.removeCursorNameToResultSetMapping (cursorName_, + section_.getServerCursorNameForPositionedUpdate()); + + // Remove client and server cursorName -> QuerySection mapping from the hashtable + // if one exists + agent_.sectionManager_.removeCursorNameToQuerySectionMapping (cursorName_, + section_.getServerCursorNameForPositionedUpdate()); + } + + // client cursor name will be set to null when it is removed from the + // clientCursorNameCache. + //cursorName_ = null; + } + + void mapCursorNameToResultSet () + { + if (cursorName_ != null) + agent_.sectionManager_.mapCursorNameToResultSet (cursorName_, resultSet_); + else + agent_.sectionManager_.mapCursorNameToResultSet (section_.getServerCursorName(), resultSet_); + } + + void parseStorProcReturnedScrollableRowset () throws SqlException + { + if (resultSetList_ != null) { + for (int i = 0; i < resultSetList_.length; i++) { + if (resultSetList_[i].scrollable_ && resultSetList_[i].cursor_.dataBufferHasUnprocessedData()) { + resultSetList_[i].parseScrollableRowset (); + if (resultSetList_[i].rowCountIsUnknown()) resultSetList_[i].getRowCount(); + } + } + } + } + + String escape (String sql) throws SqlException + { + String nativeSQL = sql; + + nativeSQL = connection_.nativeSQLX (sql); + return nativeSQL; + } + + // Called by statement constructor only for jdbc 2 statements with scroll attributes. + // This method is not in the StatementRequest class because it is not building into + // the request buffer directly, it is building into a cache to be written into the request + // buffer at prepare-time. + String cacheCursorAttributesToSendOnPrepare () throws SqlException + { + StringBuffer cursorAttributes = new StringBuffer(); + if (resultSetType_ == java.sql.ResultSet.TYPE_SCROLL_SENSITIVE) { + // append "SENSITIVE STATIC SCROLL" + cursorAttributes.append (Configuration.cursorAttribute_SensitiveStatic); + } + else if (resultSetType_ == java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE) { + // if "insensitve, updatable" cursor is asked, then server sends back error + // and we will pass that error back to the user. + // we will not try to catch any errors/warnings here. + // append "INSENSITIVE SCROLL" + cursorAttributes.append (Configuration.cursorAttribute_Insensitive); + } + + // Default is read-only, forward-only. No attribute needs to be sent. + if (resultSetConcurrency_ == java.sql.ResultSet.CONCUR_UPDATABLE) + cursorAttributes.append (Configuration.cursorAttribute_ForUpdate); // FOR UPDATE + + if ((resultSetHoldability_ == java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT)) + cursorAttributes.append (Configuration.cursorAttribute_WithHold); // WITH HOLD + + return + (cursorAttributes == null || cursorAttributes.toString().equals("")) + ? null + : cursorAttributes.toString(); + } + + protected String constructSelectFromInsertSQL (String sql) + { + String temp = "select "; + int numOfColumns = generatedKeysColumnNames_.length; + + for (int i = 0; i < numOfColumns; i++) { + temp += generatedKeysColumnNames_[i]; + if ((i+1) < numOfColumns) temp += ","; + } + temp += (" from final table (" + sql + ")"); + return temp; + } + + void getPreparedStatementForAutoGeneratedKeys () throws SqlException + { + if (preparedStatementForAutoGeneratedKeys_ == null) { + String s = "select IDENTITY_VAL_LOCAL() from SYSIBM.SYSDUMMY1"; + preparedStatementForAutoGeneratedKeys_ = + connection_.newPreparedStatement_ (s, + java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_READ_ONLY, + java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT, + java.sql.Statement.NO_GENERATED_KEYS, + null); + // need a special case for Derby, since the attribute has to go through the wire. + // This same special casing for Derby is already in place in method PS.cacheCursorAttributesToSendOnPrepare() as called by prepareStatementX(). + // We need to figure how some way to get code reuse here, ie. to consolidate to just one special casing rather than two? + // Maybe just call prepareStatementX() or use some special purpose prepare method like the ones in Connection. + // Something more abstract so that we don't have to special case the WITH HOLD thing twice. + StringBuffer cursorAttributes = new StringBuffer(); + cursorAttributes.append (Configuration.cursorAttribute_WithHold); + preparedStatementForAutoGeneratedKeys_.cursorAttributesToSendOnPrepare_ = cursorAttributes.toString(); + } +} + + void prepareAutoGeneratedKeysStatement () throws SqlException + { + getPreparedStatementForAutoGeneratedKeys(); + if (!preparedStatementForAutoGeneratedKeys_.openOnServer_) + preparedStatementForAutoGeneratedKeys_.materialPreparedStatement_.writePrepareDescribeOutput_ ( + preparedStatementForAutoGeneratedKeys_.sql_, + preparedStatementForAutoGeneratedKeys_.section_); + } + + void readPrepareAutoGeneratedKeysStatement () throws SqlException + { + if (!preparedStatementForAutoGeneratedKeys_.openOnServer_ ) + preparedStatementForAutoGeneratedKeys_.materialPreparedStatement_.readPrepareDescribeOutput_(); + } + + void checkAutoGeneratedKeysParameters () throws SqlException + { + if (autoGeneratedKeys_ != java.sql.Statement.NO_GENERATED_KEYS && + autoGeneratedKeys_ != java.sql.Statement.RETURN_GENERATED_KEYS) + throw new SqlException (agent_.logWriter_, "Invalid argument: " + + "Statement auto-generated keys value " + autoGeneratedKeys_ + + " is invalid."); + + if (generatedKeysColumnNames_ != null) + throw new SqlException (agent_.logWriter_, "Driver not capable"); + } + + public ColumnMetaData getGuessedResultSetMetaData () { + return resultSetMetaData_; + } + + public boolean isQueryMode() + { + if (this.sqlMode_ == this.isQuery__) + return true; + else + return false; + } + protected void removeClientCursorNameFromCache () + { + if (cursorName_ != null) { + connection_.clientCursorNameCache_.remove (cursorName_); + cursorName_ = null; + } + } +}
Propchange: incubator/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/derby/code/trunk/java/client/org/apache/derby/client/am/StatementCallbackInterface.java URL: http://svn.apache.org/viewcvs/incubator/derby/code/trunk/java/client/org/apache/derby/client/am/StatementCallbackInterface.java?rev=165178&view=auto ============================================================================== --- incubator/derby/code/trunk/java/client/org/apache/derby/client/am/StatementCallbackInterface.java (added) +++ incubator/derby/code/trunk/java/client/org/apache/derby/client/am/StatementCallbackInterface.java Thu Apr 28 12:05:42 2005 @@ -0,0 +1,59 @@ +/* + + Derby - Class org.apache.derby.client.am.StatementCallbackInterface + + Copyright (c) 2001, 2005 The Apache Software Foundation or its licensors, where applicable. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +package org.apache.derby.client.am; + +import org.apache.derby.client.am.Section; + +// Methods implemented by the common Statement class to handle +// certain events that may originate from the material or common layers. +// +// Reply implementations may update statement state via this interface. +// +public interface StatementCallbackInterface +{ + // A query has been opened on the server. + public void completeOpenQuery (Sqlca sqlca, ResultSet resultSet) throws DisconnectException; + public void completeExecuteCallOpenQuery (Sqlca sqlca, ResultSet resultSet, ColumnMetaData resultSetMetaData, Section generatedSection); + + // Chains a warning onto the statement. + public void accumulateWarning (SqlWarning e); + + public void completePrepare (Sqlca sqlca); + + public void completePrepareDescribeOutput (ColumnMetaData columnMetaData, Sqlca sqlca); + + public void completeExecuteImmediate (Sqlca sqlca); + + public void completeExecuteSetStatement (Sqlca sqlca); + + + public void completeExecute (Sqlca sqlca); + public void completeExecuteCall (Sqlca sqlca, Cursor params, ResultSet[] resultSets); + public void completeExecuteCall (Sqlca sqlca, Cursor params); + + public int completeSqlca (Sqlca sqlca); + + public ConnectionCallbackInterface getConnectionCallbackInterface (); + + public ColumnMetaData getGuessedResultSetMetaData (); + + +} Propchange: incubator/derby/code/trunk/java/client/org/apache/derby/client/am/StatementCallbackInterface.java ------------------------------------------------------------------------------ svn:eol-style = native