You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ba...@apache.org on 2005/04/28 21:05:45 UTC
svn commit: r165178 [13/26] - in /incubator/derby/code/trunk: ./
java/client/ java/client/org/ java/client/org/apache/
java/client/org/apache/derby/ java/client/org/apache/derby/client/
java/client/org/apache/derby/client/am/
java/client/org/apache/derby/client/net/
java/client/org/apache/derby/client/resources/
java/client/org/apache/derby/jdbc/ tools/ant/properties/
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