0001: /*
0002:
0003: Derby - Class org.apache.derby.client.am.Statement
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to You under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021: package org.apache.derby.client.am;
0022:
0023: import java.sql.SQLException;
0024:
0025: import org.apache.derby.shared.common.reference.JDBC30Translation;
0026: import org.apache.derby.shared.common.reference.SQLState;
0027:
0028: public class Statement implements java.sql.Statement,
0029: StatementCallbackInterface {
0030:
0031: // JDBC 3 constant indicating that the current ResultSet object
0032: // should be closed when calling getMoreResults.
0033: // Constant value matches that defined by JDBC 3 java.sql.Statement.CLOSE_CURRENT_RESULT
0034: public final static int CLOSE_CURRENT_RESULT = 1;
0035:
0036: // JDBC 3 constant indicating that the current ResultSet object
0037: // should not be closed when calling getMoreResults.
0038: // Constant value matches that defined by JDBC 3 java.sql.Statement.KEEP_CURRENT_RESULT
0039: public final static int KEEP_CURRENT_RESULT = 2;
0040:
0041: // JDBC 3 constant indicating that all ResultSet objects that
0042: // have previously been kept open should be closed when calling getMoreResults.
0043: // Constant value matches that defined by JDBC 3 java.sql.Statement.CLOSE_ALL_RESULTS
0044: public final static int CLOSE_ALL_RESULTS = 3;
0045:
0046: //---------------------navigational members-----------------------------------
0047:
0048: public MaterialStatement materialStatement_ = null;
0049:
0050: public Connection connection_;
0051: public Section section_;
0052: public Agent agent_;
0053:
0054: public ResultSet resultSet_ = null;
0055:
0056: // Use -1, if there is no update count returned, ie. when result set is returned. 0 is a valid update count for DDL.
0057: int updateCount_ = -1;
0058: int returnValueFromProcedure_;
0059:
0060: // Enumeration of the flavors of statement execute call used.
0061: static final int executeQueryMethod__ = 1;
0062: static final int executeUpdateMethod__ = 2;
0063: static final int executeMethod__ = 3;
0064:
0065: // sqlMode_ will be moved to PS as soon as we remove the hack reference in completeExecute()
0066: // Enumerated in Statement: S.sqlIsQuery__, S.sqlIsCall__, S.sqlIsUpdate__
0067: // Determines whether sql_ starts with SELECT/VALUES, CALL, or other (assumed to be an update).
0068: protected int sqlMode_ = 0;
0069: // Enum for sqlMode_:
0070: static final int isQuery__ = 0x1; // sql starts with SELECT.... or VALUES...
0071: static final int isCall__ = 0x2; // sql starts with CALL ...
0072: static final int isUpdate__ = 0x4; // All other sql is categorized as a update DML or DDL.
0073:
0074: // sqlUpdateMode_ is only set when the sqlMode_ == isUpdate__
0075: public int sqlUpdateMode_ = 0;
0076: // Enum for sqlUpdateMode_:
0077: public final static int isCommitSql__ = 0x1;
0078: public final static int isRollbackSql__ = 0x2;
0079: final static int isPositionedUpdateDeleteSql__ = 0x10;
0080: final static int isInsertSql__ = 0x20; // used to recognize "insert" for auto-generated keys
0081: final static int isDeleteSql__ = 0x40; // used to recognize "delete" for parsing cursorname
0082: final static int isUpdateSql__ = 0x80; // used to recognize "update" for parsing cursorname
0083:
0084: public ColumnMetaData resultSetMetaData_; // type information for output sqlda
0085:
0086: // these two are used during parsing of literals for call statement.
0087: // please add a comment desribing what why you can't reuse inputs_ and parameterMetaData_
0088: // members for the literal inputs
0089:
0090: // Caching the Cursor object for reuse.
0091: public Cursor cachedCursor_ = null;
0092: public Cursor cachedSingletonRowData_ = null;
0093: public boolean isPreparedStatement_ = false;
0094: public boolean isCallableStatement_ = false; // we can get rid of this member once we define polymorphic reset() on S/PS/CS
0095:
0096: //---------------------navigational cheat-links-------------------------------
0097: // Cheat-links are for convenience only, and are not part of the conceptual model.
0098: // Warning:
0099: // Cheat-links should only be defined for invariant state data.
0100: // That is, state data that is set by the constructor and never changes.
0101:
0102: // Alias for connection_.databaseMetaData
0103: public DatabaseMetaData databaseMetaData_;
0104:
0105: //-----------------------------state------------------------------------------
0106:
0107: // Jdbc 1 positioned updates are implemented via
0108: // sql scan for "...where current of <users-cursor-name>",
0109: // the addition of mappings from cursor names to query sections,
0110: // and the subtitution of <users-cursor-name> with <canned-cursor-name> in the pass-thru sql string
0111: // "...where current of <canned-cursor-name>" when user-defined cursor names are used.
0112: // Both "canned" cursor names (from our jdbc package set) and user-defined cursor names are mapped.
0113: // Statement.cursorName_ is initialized to null until the cursor name is requested or set.
0114: // s.setCursorName()) adds a user-defined name, but it is not
0115: // added to the cursor map until execution time (DERBY-1036);
0116: // When requested (rs.getCursorName()), if the cursor name is still null,
0117: // then is given the canned cursor name as defined by our jdbc package set.
0118: // Still need to consider how positioned updates should interact with multiple result sets from a stored.
0119: String cursorName_ = null;
0120:
0121: // This means the client-side jdbc statement object is open.
0122: // This value is set to true when the statement object is constructed, and will not change
0123: // until statement.close() is called either directly or via connection.close(), finalizer, or other methods.
0124: boolean openOnClient_ = true;
0125: // This means a DERBY server-side section for this statement is in the prepared state.
0126: // A client-side jdbc statement may remain open across commits (openOnClient=true),
0127: // but the server-side DERBY section moves to an unprepared state (openOnServer=false) across commits,
0128: // requiring an implicit re-prepare "under the covers" by the driver.
0129: // Unprepared jdbc query statements still have prepared sections on the server.
0130: // This openOnServer_ only has implications for preparedstatement
0131: boolean openOnServer_ = false;
0132:
0133: //private int indexOfCurrentResultSet_ = -1;
0134: protected int indexOfCurrentResultSet_ = -1;
0135: ResultSet[] resultSetList_ = null; // array of ResultSet objects
0136:
0137: protected final static String TIMEOUT_STATEMENT = "SET STATEMENT_TIMEOUT ";
0138: protected java.util.ArrayList timeoutArrayList = new java.util.ArrayList(
0139: 1);
0140: protected boolean doWriteTimeout = false;
0141: int timeout_ = 0; // for query timeout in seconds
0142: int maxRows_ = 0;
0143: int maxFieldSize_ = 0; // zero means that there is no limit to the size of a column.
0144: boolean escapedProcedureCallWithResult_ = false;
0145:
0146: // When this is false we skip autocommit for this PreparedStatement.
0147: // This is needed when the PreparedStatement object is used internally by
0148: // the driver and a commit is not desired, e.g., Blob/Clob API calls
0149: public boolean isAutoCommittableStatement_ = true;
0150:
0151: // The user has no control over the statement that owns a catalog query, and has no ability to close that statement.
0152: // We need a special member variable on our internal catalog query statements so that
0153: // when the catalog query is closed, the result set will know to close it's owning statement.
0154: boolean isCatalogQuery_ = false;
0155:
0156: // This collection is used for two different purposes:
0157: // For statement batching it contains the batched SQL strings.
0158: // For prepared statement batching it contains the batched input rows.
0159: java.util.ArrayList batch_ = new java.util.ArrayList();
0160:
0161: // Scrollable cursor attributes
0162: public int resultSetType_ = java.sql.ResultSet.TYPE_FORWARD_ONLY;
0163: public int resultSetConcurrency_ = java.sql.ResultSet.CONCUR_READ_ONLY;
0164: public int resultSetHoldability_;
0165: // This is ignored by the driver if this is zero.
0166: // 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.
0167: // For the net scrollable result set, then we use a default of 64 rows.
0168: public int fetchSize_ = 0;
0169: public int fetchDirection_ = java.sql.ResultSet.FETCH_FORWARD;
0170:
0171: // Conceptually this doesn't belong in Statement, but belongs in PreparedStatement,
0172: // since Statement doesn't know about params, so we're just putting it here perhaps temporarily,
0173: // Used for callable statement OUT paramters.
0174: public Cursor singletonRowData_ = null;
0175:
0176: // number of invisible result sets returned from a stored procedure.
0177: public int numInvisibleRS_ = 0;
0178:
0179: // This is a cache of the attributes to be sent on prepare.
0180: // Think about caching the entire prepare DDM string for the re-prepares
0181: public String cursorAttributesToSendOnPrepare_ = null;
0182:
0183: // The following members are for the exclusive use of prepared statements that require auto-generated keys to be returned
0184: public PreparedStatement preparedStatementForAutoGeneratedKeys_;
0185: public ResultSet generatedKeysResultSet_;
0186: public String[] generatedKeysColumnNames_;
0187: public int autoGeneratedKeys_ = java.sql.Statement.NO_GENERATED_KEYS;
0188:
0189: // This flag makes sure that only one copy of this statement
0190: // will be in connection_.commitListeners_.
0191:
0192: private SqlWarning warnings_ = null;
0193:
0194: // A Statement is NOT poolable by default. The constructor for
0195: // PreparedStatement overrides this.
0196: protected boolean isPoolable = false;
0197:
0198: //---------------------constructors/finalizer/accessors--------------------
0199:
0200: private Statement() throws SqlException {
0201: initStatement();
0202: }
0203:
0204: private void resetStatement() throws SqlException {
0205: initStatement();
0206: }
0207:
0208: private void initStatement() throws SqlException {
0209: materialStatement_ = null;
0210: connection_ = null;
0211: agent_ = null;
0212: databaseMetaData_ = null;
0213: resultSetType_ = java.sql.ResultSet.TYPE_FORWARD_ONLY;
0214: resultSetConcurrency_ = java.sql.ResultSet.CONCUR_READ_ONLY;
0215: resultSetHoldability_ = 0;
0216: cursorAttributesToSendOnPrepare_ = null;
0217: if (timeoutArrayList.size() == 0) {
0218: timeoutArrayList.add(null); // Make sure the list's length is 1
0219: }
0220:
0221: initResetStatement();
0222: }
0223:
0224: private void initResetStatement() throws SqlException {
0225: initResetPreparedStatement();
0226:
0227: //section_ = null; // don't set section to null because write piggyback command require a section
0228: if (section_ != null) {
0229: section_.free();
0230: }
0231: sqlMode_ = 0;
0232: sqlUpdateMode_ = 0;
0233: resultSetMetaData_ = null;
0234: }
0235:
0236: protected void initResetPreparedStatement() {
0237: warnings_ = null;
0238: //section_ = null;
0239: resultSet_ = null;
0240: updateCount_ = -1;
0241: returnValueFromProcedure_ = 0;
0242: cursorName_ = null;
0243: openOnClient_ = true;
0244: openOnServer_ = false;
0245: indexOfCurrentResultSet_ = -1;
0246: resultSetList_ = null;
0247: timeout_ = 0;
0248: doWriteTimeout = false;
0249: maxRows_ = 0;
0250: maxFieldSize_ = 0;
0251: escapedProcedureCallWithResult_ = false;
0252: isCatalogQuery_ = false;
0253: isAutoCommittableStatement_ = true;
0254:
0255: if (batch_ == null) {
0256: batch_ = new java.util.ArrayList();
0257: } else {
0258: batch_.clear();
0259: }
0260: fetchSize_ = 0;
0261: fetchDirection_ = java.sql.ResultSet.FETCH_FORWARD;
0262: singletonRowData_ = null;
0263: numInvisibleRS_ = 0;
0264: preparedStatementForAutoGeneratedKeys_ = null;
0265: generatedKeysResultSet_ = null;
0266: generatedKeysColumnNames_ = null;
0267: autoGeneratedKeys_ = java.sql.Statement.NO_GENERATED_KEYS;
0268:
0269: // these members were not initialized
0270: isPreparedStatement_ = false;
0271: }
0272:
0273: // If a dataSource is passed into resetClientConnection(), then we will assume
0274: // properties on the dataSource may have changed, and we will need to go through
0275: // the open-statement list on the connection and do a full reset on all statements,
0276: // including preparedStatement's and callableStatement's. This is because property
0277: // change may influence the section we allocate for the preparedStatement, and
0278: // also the cursor attributes, i.e. setCursorSensitivity().
0279: // If no dataSource is passed into resetClientConnection(), then we will do the
0280: // minimum reset required for preparedStatement's and callableStatement's.
0281: public void reset(boolean fullReset) throws SqlException {
0282: if (fullReset) {
0283: connection_.resetStatement(this );
0284: } else {
0285: initResetStatement();
0286: materialStatement_.reset_();
0287: }
0288: }
0289:
0290: public Statement(Agent agent, Connection connection)
0291: throws SqlException {
0292: this ();
0293: initStatement(agent, connection);
0294: }
0295:
0296: public void resetStatement(Agent agent, Connection connection)
0297: throws SqlException {
0298: resetStatement();
0299: initStatement(agent, connection);
0300: }
0301:
0302: private void initStatement(Agent agent, Connection connection) {
0303: agent_ = agent;
0304: connection_ = connection;
0305: databaseMetaData_ = connection.databaseMetaData_;
0306: }
0307:
0308: // For jdbc 2 statements with scroll attributes
0309: public Statement(Agent agent, Connection connection, int type,
0310: int concurrency, int holdability, int autoGeneratedKeys,
0311: String[] columnNames) throws SqlException {
0312: this (agent, connection);
0313: initStatement(type, concurrency, holdability,
0314: autoGeneratedKeys, columnNames);
0315: }
0316:
0317: public void resetStatement(Agent agent, Connection connection,
0318: int type, int concurrency, int holdability,
0319: int autoGeneratedKeys, String[] columnNames)
0320: throws SqlException {
0321: resetStatement(agent, connection);
0322: initStatement(type, concurrency, holdability,
0323: autoGeneratedKeys, columnNames);
0324: }
0325:
0326: private void initStatement(int type, int concurrency,
0327: int holdability, int autoGeneratedKeys, String[] columnNames)
0328: throws SqlException {
0329: switch (type) {
0330: case java.sql.ResultSet.TYPE_FORWARD_ONLY:
0331: case java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE:
0332: case java.sql.ResultSet.TYPE_SCROLL_SENSITIVE:
0333: resultSetType_ = type;
0334: break;
0335: default:
0336: throw new SqlException(
0337: agent_.logWriter_,
0338: new ClientMessageId(SQLState.INVALID_API_PARAMETER),
0339: new Integer(type), "type", "createStatement()");
0340: }
0341:
0342: switch (concurrency) {
0343: case java.sql.ResultSet.CONCUR_READ_ONLY:
0344: case java.sql.ResultSet.CONCUR_UPDATABLE:
0345: resultSetConcurrency_ = concurrency;
0346: break;
0347: default:
0348: throw new SqlException(
0349: agent_.logWriter_,
0350: new ClientMessageId(SQLState.INVALID_API_PARAMETER),
0351: new Integer(concurrency), "concurrency",
0352: "createStatement()");
0353: }
0354:
0355: switch (holdability) {
0356: case JDBC30Translation.CLOSE_CURSORS_AT_COMMIT:
0357: case JDBC30Translation.HOLD_CURSORS_OVER_COMMIT:
0358: resultSetHoldability_ = holdability;
0359: break;
0360: default:
0361: throw new SqlException(
0362: agent_.logWriter_,
0363: new ClientMessageId(SQLState.INVALID_API_PARAMETER),
0364: new Integer(holdability), "holdability",
0365: "createStatement()");
0366: }
0367:
0368: switch (autoGeneratedKeys) {
0369: case java.sql.Statement.NO_GENERATED_KEYS:
0370: case java.sql.Statement.RETURN_GENERATED_KEYS:
0371: autoGeneratedKeys_ = autoGeneratedKeys;
0372: break;
0373: default:
0374: throw new SqlException(
0375: agent_.logWriter_,
0376: new ClientMessageId(SQLState.INVALID_API_PARAMETER),
0377: new Integer(autoGeneratedKeys),
0378: "autoGeneratedKeys", "createStatement");
0379: }
0380:
0381: generatedKeysColumnNames_ = columnNames;
0382: }
0383:
0384: /* (non-Javadoc)
0385: * @see java.lang.Object#finalize()
0386: *
0387: * This method cleans up client-side resources by calling markClosed().
0388: * It is different from close() method, which also does clean up on server.
0389: * Changes done as part of DERBY-210.
0390: */
0391: protected void finalize() throws java.lang.Throwable {
0392: if (agent_.loggingEnabled()) {
0393: agent_.logWriter_.traceEntry(this , "finalize");
0394: }
0395: if (openOnClient_) {
0396: markClosed();
0397: }
0398: super .finalize();
0399: }
0400:
0401: /*
0402: * Accessor to state variable warnings_
0403: */
0404: protected SqlWarning getSqlWarnings() {
0405: return warnings_;
0406: }
0407:
0408: // ---------------------------jdbc 1------------------------------------------
0409:
0410: public java.sql.ResultSet executeQuery(String sql)
0411: throws SQLException {
0412: try {
0413: synchronized (connection_) {
0414: if (agent_.loggingEnabled()) {
0415: agent_.logWriter_.traceEntry(this , "executeQuery",
0416: sql);
0417: }
0418: ResultSet resultSet = executeQueryX(sql);
0419: if (agent_.loggingEnabled()) {
0420: agent_.logWriter_.traceExit(this , "executeQuery",
0421: resultSet);
0422: }
0423: return resultSet;
0424: }
0425: } catch (SqlException se) {
0426: throw se.getSQLException();
0427: }
0428:
0429: }
0430:
0431: private ResultSet executeQueryX(String sql) throws SqlException {
0432: flowExecute(executeQueryMethod__, sql);
0433: return resultSet_;
0434: }
0435:
0436: public int executeUpdate(String sql) throws SQLException {
0437: try {
0438: synchronized (connection_) {
0439: if (agent_.loggingEnabled()) {
0440: agent_.logWriter_.traceEntry(this , "executeUpdate",
0441: sql);
0442: }
0443: int updateValue = executeUpdateX(sql);
0444: if (agent_.loggingEnabled()) {
0445: agent_.logWriter_.traceExit(this , "executeUpdate",
0446: updateValue);
0447: }
0448: return updateValue;
0449: }
0450: } catch (SqlException se) {
0451: throw se.getSQLException();
0452: }
0453: }
0454:
0455: private int executeUpdateX(String sql) throws SqlException {
0456: flowExecute(executeUpdateMethod__, sql);
0457: return updateCount_;
0458: }
0459:
0460: /**
0461: * Returns false unless <code>iface</code> is implemented
0462: *
0463: * @param iface a Class defining an interface.
0464: * @return true if this implements the interface or
0465: * directly or indirectly wraps an object
0466: * that does.
0467: * @throws java.sql.SQLException if an error occurs while determining
0468: * whether this is a wrapper for an object
0469: * with the given interface.
0470: */
0471: public boolean isWrapperFor(Class iface) throws SQLException {
0472: try {
0473: checkForClosedStatement();
0474: } catch (SqlException se) {
0475: throw se.getSQLException();
0476: }
0477: return iface.isInstance(this );
0478: }
0479:
0480: /**
0481: * Tell whether the statement has been closed or not.
0482: *
0483: * @return <code>true</code> if closed, <code>false</code> otherwise.
0484: * @exception SQLException if a database access error occurs (according to
0485: * spec). Never thrown by this implementation.
0486: */
0487: public boolean isClosed() throws SQLException {
0488: if (agent_.loggingEnabled()) {
0489: agent_.logWriter_.traceEntry(this , "isClosed",
0490: !openOnClient_);
0491: }
0492: if (agent_.loggingEnabled()) {
0493: agent_.logWriter_.traceExit(this , "isClosed",
0494: !openOnClient_);
0495: }
0496: return !openOnClient_;
0497: }
0498:
0499: // The server holds statement resources until transaction end.
0500: public void close() throws SQLException {
0501: try {
0502: synchronized (connection_) {
0503: if (agent_.loggingEnabled()) {
0504: agent_.logWriter_.traceEntry(this , "close");
0505: }
0506: closeX();
0507: }
0508: } catch (SqlException se) {
0509: throw se.getSQLException();
0510: }
0511: }
0512:
0513: /**
0514: * An untraced version of <code>close</code>. This method cleans up
0515: * client-side resources and also sends commands to network server to
0516: * perform clean up. This should not be called in the finalizer.
0517: * Difference between <code>finalize</code> and <code>close</code> is
0518: * that close method does these things additionally (Changes done as
0519: * part of DERBY-210):
0520: * 1) Sends commands to the server to close the result sets.
0521: * 2) Sends commands to the server to close the result sets of the
0522: * generated keys query.
0523: * 3) Sends a commit if autocommit is on and it is appropriate.
0524: * 4) Explicitly removes the statement from connection_.openStatements_
0525: * and CommitAndRollbackListeners_ by passing true to markClosed.
0526: *
0527: * We may need to do 1) in finalizer too. This is being tracked in
0528: * DERBY-1021
0529: *
0530: * @throws SqlException
0531: */
0532: public void closeX() throws SqlException {
0533: if (!openOnClient_) {
0534: return;
0535: }
0536: // Regardless of whether or not this statement is in the prepared state,
0537: // we need to close any open cursors for this statement on the server.
0538: int numberOfResultSetsToClose = (resultSetList_ == null) ? 0
0539: : resultSetList_.length;
0540: boolean willTickleServer = willTickleServer(
0541: numberOfResultSetsToClose, true);
0542: try {
0543: if (willTickleServer) {
0544: flowClose();
0545: } else {
0546: flowCloseOutsideUOW();
0547: }
0548: } finally {
0549: markClosed(true);
0550: }
0551: }
0552:
0553: /**
0554: * Returns the value of the poolable hint, indicating whether
0555: * pooling is requested.
0556: *
0557: * @return The value of the poolable hint.
0558: * @throws SQLException if the Statement has been closed.
0559: */
0560: public boolean isPoolable() throws SQLException {
0561: try {
0562: synchronized (connection_) {
0563: if (agent_.loggingEnabled()) {
0564: agent_.logWriter_.traceEntry(this , "isPoolable");
0565: }
0566: // Assert the statement has not been closed
0567: checkForClosedStatement();
0568:
0569: return isPoolable;
0570: }
0571: } catch (SqlException se) {
0572: throw se.getSQLException();
0573: }
0574: }
0575:
0576: /**
0577: * Requests that a Statement be pooled or not.
0578: *
0579: * @param poolable requests that the Statement be pooled if true
0580: * and not be pooled if false.
0581: * @throws SQLException if the Statement has been closed.
0582: */
0583: public void setPoolable(boolean poolable) throws SQLException {
0584: try {
0585: synchronized (connection_) {
0586: if (agent_.loggingEnabled()) {
0587: agent_.logWriter_.traceEntry(this , "setPoolable",
0588: poolable);
0589: }
0590: // Assert the statement has not been closed
0591: checkForClosedStatement();
0592:
0593: isPoolable = poolable;
0594: }
0595: } catch (SqlException se) {
0596: throw se.getSQLException();
0597: }
0598: }
0599:
0600: public int getMaxFieldSize() throws SQLException {
0601: try {
0602: if (agent_.loggingEnabled()) {
0603: agent_.logWriter_.traceEntry(this , "getMaxFieldSize");
0604: }
0605: checkForClosedStatement();
0606: return maxFieldSize_;
0607: } catch (SqlException se) {
0608: throw se.getSQLException();
0609: }
0610: }
0611:
0612: public void setMaxFieldSize(int max) throws SQLException {
0613: try {
0614: synchronized (connection_) {
0615: if (agent_.loggingEnabled()) {
0616: agent_.logWriter_.traceEntry(this ,
0617: "setMaxFieldSize", max);
0618: }
0619: checkForClosedStatement();
0620: if (max < 0) {
0621: throw new SqlException(agent_.logWriter_,
0622: new ClientMessageId(
0623: SQLState.INVALID_MAXFIELD_SIZE),
0624: new Integer(max));
0625: }
0626: maxFieldSize_ = max;
0627: }
0628: } catch (SqlException se) {
0629: throw se.getSQLException();
0630: }
0631: }
0632:
0633: public int getMaxRows() throws SQLException {
0634: try {
0635: checkForClosedStatement();
0636: if (agent_.loggingEnabled()) {
0637: agent_.logWriter_.traceExit(this , "getMaxRows",
0638: maxRows_);
0639: }
0640: return maxRows_;
0641: } catch (SqlException se) {
0642: throw se.getSQLException();
0643: }
0644: }
0645:
0646: public void setMaxRows(int maxRows) throws SQLException {
0647: try {
0648: synchronized (connection_) {
0649: if (agent_.loggingEnabled()) {
0650: agent_.logWriter_.traceEntry(this , "setMaxRows",
0651: maxRows);
0652: }
0653: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
0654: if (maxRows < 0) {
0655: throw new SqlException(agent_.logWriter_,
0656: new ClientMessageId(
0657: SQLState.INVALID_MAX_ROWS_VALUE),
0658: new Integer(maxRows));
0659: }
0660: maxRows_ = maxRows;
0661: }
0662: } catch (SqlException se) {
0663: throw se.getSQLException();
0664: }
0665: }
0666:
0667: public void setEscapeProcessing(boolean enable) throws SQLException {
0668: try {
0669: synchronized (connection_) {
0670: if (agent_.loggingEnabled()) {
0671: agent_.logWriter_.traceEntry(this ,
0672: "setEscapeProcessing", enable);
0673: }
0674: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
0675: }
0676: } catch (SqlException se) {
0677: throw se.getSQLException();
0678: }
0679: }
0680:
0681: public int getQueryTimeout() throws SQLException {
0682: try {
0683: checkForClosedStatement();
0684: if (agent_.loggingEnabled()) {
0685: agent_.logWriter_.traceExit(this , "getQueryTimeout",
0686: timeout_);
0687: }
0688: return timeout_;
0689: } catch (SqlException se) {
0690: throw se.getSQLException();
0691: }
0692: }
0693:
0694: public void setQueryTimeout(int seconds) throws SQLException {
0695: try {
0696: synchronized (connection_) {
0697: if (agent_.loggingEnabled()) {
0698: agent_.logWriter_.traceEntry(this ,
0699: "setQueryTimeout", seconds);
0700: }
0701: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
0702: if (seconds < 0) {
0703: throw new SqlException(
0704: agent_.logWriter_,
0705: new ClientMessageId(
0706: SQLState.INVALID_QUERYTIMEOUT_VALUE),
0707: new Integer(seconds));
0708: }
0709: if (seconds != timeout_) {
0710: timeout_ = seconds;
0711: doWriteTimeout = true;
0712: }
0713: }
0714: } catch (SqlException se) {
0715: throw se.getSQLException();
0716: }
0717: }
0718:
0719: public void cancel() throws SQLException {
0720: try {
0721: if (agent_.loggingEnabled()) {
0722: agent_.logWriter_.traceEntry(this , "cancel");
0723: }
0724: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
0725: throw new SqlException(agent_.logWriter_,
0726: new ClientMessageId(
0727: SQLState.CANCEL_NOT_SUPPORTED_BY_SERVER));
0728: } catch (SqlException se) {
0729: throw se.getSQLException();
0730: }
0731: }
0732:
0733: public java.sql.SQLWarning getWarnings() throws SQLException {
0734: if (agent_.loggingEnabled()) {
0735: agent_.logWriter_.traceExit(this , "getWarnings", warnings_);
0736: }
0737: try {
0738: checkForClosedStatement();
0739: } catch (SqlException se) {
0740: throw se.getSQLException();
0741: }
0742: return warnings_ == null ? null : warnings_.getSQLWarning();
0743: }
0744:
0745: public void clearWarnings() throws SQLException {
0746: synchronized (connection_) {
0747: if (agent_.loggingEnabled()) {
0748: agent_.logWriter_.traceEntry(this , "clearWarnings");
0749: }
0750: try {
0751: checkForClosedStatement();
0752: } catch (SqlException se) {
0753: throw se.getSQLException();
0754: }
0755: clearWarningsX();
0756: }
0757: }
0758:
0759: // An untraced version of clearWarnings()
0760: public void clearWarningsX() {
0761: warnings_ = null;
0762: }
0763:
0764: // Dnc statements are already associated with a unique cursor name as defined
0765: // by our canned dnc package set.
0766: // ResultSet.getCursorName() should be used to
0767: // obtain the for update cursor name to use when executing a positioned update statement.
0768: // See Jdbc 3 spec section 14.2.4.4.
0769: public void setCursorName(String name) throws SQLException {
0770: try {
0771: synchronized (connection_) {
0772: if (agent_.loggingEnabled()) {
0773: agent_.logWriter_.traceEntry(this , "setCursorName",
0774: name);
0775: }
0776: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
0777: if (name == null || name.equals("")) {
0778: throw new SqlException(agent_.logWriter_,
0779: new ClientMessageId(
0780: SQLState.CURSOR_INVALID_NAME), name);
0781: }
0782:
0783: // Invalid to set the cursor name if there are ResultSet's open on the Statement.
0784: if (resultSet_ != null && resultSet_.openOnClient_) {
0785: throw new SqlException(
0786: agent_.logWriter_,
0787: new ClientMessageId(
0788: SQLState.LANG_CANT_INVALIDATE_OPEN_RESULT_SET),
0789: "setCursorName()", "Statement");
0790: }
0791:
0792: // DERBY-1036: Duplicate cursor names not allowed, check
0793: // deferred till execute time.
0794:
0795: cursorName_ = name;
0796: }
0797: } catch (SqlException se) {
0798: throw se.getSQLException();
0799: }
0800: }
0801:
0802: //----------------------- Multiple Results --------------------------
0803:
0804: public boolean execute(String sql) throws SQLException {
0805: try {
0806: synchronized (connection_) {
0807: if (agent_.loggingEnabled()) {
0808: agent_.logWriter_.traceEntry(this , "execute", sql);
0809: }
0810: boolean b = executeX(sql);
0811: if (agent_.loggingEnabled()) {
0812: agent_.logWriter_.traceExit(this , "execute", b);
0813: }
0814: return b;
0815: }
0816: } catch (SqlException se) {
0817: throw se.getSQLException();
0818: }
0819: }
0820:
0821: boolean executeX(String sql) throws SqlException {
0822: flowExecute(executeMethod__, sql);
0823: return resultSet_ != null;
0824: }
0825:
0826: public java.sql.ResultSet getResultSet() throws SQLException {
0827: try {
0828: synchronized (connection_) {
0829: if (agent_.loggingEnabled()) {
0830: agent_.logWriter_.traceEntry(this , "getResultSet");
0831: }
0832: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
0833: if (agent_.loggingEnabled()) {
0834: agent_.logWriter_.traceExit(this , "getResultSet",
0835: resultSet_);
0836: }
0837: return resultSet_;
0838: }
0839: } catch (SqlException se) {
0840: throw se.getSQLException();
0841: }
0842: }
0843:
0844: public int getUpdateCount() throws SQLException {
0845: try {
0846: synchronized (connection_) {
0847: if (agent_.loggingEnabled()) {
0848: agent_.logWriter_
0849: .traceEntry(this , "getUpdateCount");
0850: }
0851: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
0852: if (agent_.loggingEnabled()) {
0853: agent_.logWriter_.traceExit(this , "getUpdateCount",
0854: updateCount_);
0855: }
0856: return updateCount_;
0857: }
0858: } catch (SqlException se) {
0859: throw se.getSQLException();
0860: }
0861: }
0862:
0863: public boolean getMoreResults() throws SQLException {
0864: try {
0865: synchronized (connection_) {
0866: if (agent_.loggingEnabled()) {
0867: agent_.logWriter_
0868: .traceEntry(this , "getMoreResults");
0869: }
0870: boolean resultIsResultSet = getMoreResultsX(CLOSE_ALL_RESULTS);
0871: if (agent_.loggingEnabled()) {
0872: agent_.logWriter_.traceExit(this , "getMoreResults",
0873: resultIsResultSet);
0874: }
0875: return resultIsResultSet;
0876: }
0877: } catch (SqlException se) {
0878: throw se.getSQLException();
0879: }
0880: }
0881:
0882: //--------------------------JDBC 2.0-----------------------------
0883:
0884: public void setFetchDirection(int direction) throws SQLException {
0885: try {
0886: synchronized (connection_) {
0887: if (agent_.loggingEnabled()) {
0888: agent_.logWriter_.traceEntry(this ,
0889: "setFetchDirection", direction);
0890: }
0891: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
0892: switch (direction) {
0893: case java.sql.ResultSet.FETCH_FORWARD:
0894: case java.sql.ResultSet.FETCH_REVERSE:
0895: case java.sql.ResultSet.FETCH_UNKNOWN:
0896: fetchDirection_ = direction;
0897: break;
0898: default:
0899: throw new SqlException(agent_.logWriter_,
0900: new ClientMessageId(
0901: SQLState.INVALID_FETCH_DIRECTION),
0902: new Integer(direction));
0903: }
0904: }
0905: } catch (SqlException se) {
0906: throw se.getSQLException();
0907: }
0908: }
0909:
0910: public int getFetchDirection() throws SQLException {
0911: try {
0912: checkForClosedStatement();
0913: if (agent_.loggingEnabled()) {
0914: agent_.logWriter_.traceExit(this , "getFetchDirection",
0915: fetchDirection_);
0916: }
0917: return fetchDirection_;
0918: } catch (SqlException se) {
0919: throw se.getSQLException();
0920: }
0921: }
0922:
0923: public void setFetchSize(int rows) throws SQLException {
0924: try {
0925: synchronized (connection_) {
0926: if (agent_.loggingEnabled()) {
0927: agent_.logWriter_.traceEntry(this , "setFetchSize",
0928: rows);
0929: }
0930: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
0931:
0932: if (rows < 0 || (maxRows_ != 0 && rows > maxRows_)) {
0933: throw new SqlException(agent_.logWriter_,
0934: new ClientMessageId(
0935: SQLState.INVALID_ST_FETCH_SIZE),
0936: new Integer(rows)).getSQLException();
0937: }
0938: fetchSize_ = rows;
0939: }
0940: } catch (SqlException se) {
0941: throw se.getSQLException();
0942: }
0943: }
0944:
0945: public int getFetchSize() throws SQLException {
0946: try {
0947: checkForClosedStatement();
0948: if (agent_.loggingEnabled()) {
0949: agent_.logWriter_.traceExit(this , "getFetchSize",
0950: fetchSize_);
0951: }
0952: return fetchSize_;
0953: } catch (SqlException se) {
0954: throw se.getSQLException();
0955: }
0956: }
0957:
0958: public int getResultSetConcurrency() throws SQLException {
0959: try {
0960: checkForClosedStatement();
0961: if (agent_.loggingEnabled()) {
0962: agent_.logWriter_.traceExit(this ,
0963: "getResultSetConcurrency",
0964: resultSetConcurrency_);
0965: }
0966: return resultSetConcurrency_;
0967: } catch (SqlException se) {
0968: throw se.getSQLException();
0969: }
0970: }
0971:
0972: public int getResultSetType() throws SQLException {
0973: try {
0974: checkForClosedStatement();
0975: if (agent_.loggingEnabled()) {
0976: agent_.logWriter_.traceExit(this , "getResultSetType",
0977: resultSetType_);
0978: }
0979: return resultSetType_;
0980: } catch (SqlException se) {
0981: throw se.getSQLException();
0982: }
0983: }
0984:
0985: public void addBatch(String sql) throws SQLException {
0986: try {
0987: synchronized (connection_) {
0988: if (agent_.loggingEnabled()) {
0989: agent_.logWriter_.traceEntry(this , "addBatch", sql);
0990: }
0991: checkForClosedStatement();
0992: sql = connection_.nativeSQLX(sql);
0993: batch_.add(sql);
0994: }
0995: } catch (SqlException se) {
0996: throw se.getSQLException();
0997: }
0998: }
0999:
1000: public void clearBatch() throws SQLException {
1001: try {
1002: synchronized (connection_) {
1003: if (agent_.loggingEnabled()) {
1004: agent_.logWriter_.traceEntry(this , "clearBatch");
1005: }
1006: checkForClosedStatement();
1007: batch_.clear();
1008: }
1009: } catch (SqlException se) {
1010: throw se.getSQLException();
1011: }
1012: }
1013:
1014: public int[] executeBatch() throws SQLException,
1015: BatchUpdateException {
1016: try {
1017: synchronized (connection_) {
1018: if (agent_.loggingEnabled()) {
1019: agent_.logWriter_.traceEntry(this , "executeBatch");
1020: }
1021: int[] updateCounts = executeBatchX();
1022: if (agent_.loggingEnabled()) {
1023: agent_.logWriter_.traceExit(this , "executeBatch",
1024: updateCounts);
1025: }
1026: return updateCounts;
1027: }
1028: } catch (SqlException se) {
1029: throw se.getSQLException();
1030: }
1031: }
1032:
1033: private int[] executeBatchX() throws SqlException,
1034: BatchUpdateException {
1035: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
1036: clearWarningsX(); // Per jdbc spec 0.7, and getWarnings() javadoc
1037: resultSetList_ = null;
1038: // Initialize all the updateCounts to indicate failure
1039: // This is done to account for "chain-breaking" errors where we cannot
1040: // read any more replies
1041: int[] updateCounts = new int[batch_.size()];
1042: for (int i = 0; i < batch_.size(); i++) {
1043: updateCounts[i] = -3;
1044: }
1045: flowExecuteBatch(updateCounts);
1046: return updateCounts;
1047: }
1048:
1049: public java.sql.Connection getConnection() throws SQLException {
1050: try {
1051: checkForClosedStatement();
1052: if (agent_.loggingEnabled()) {
1053: agent_.logWriter_.traceExit(this , "getConnection",
1054: connection_);
1055: }
1056: return connection_;
1057: } catch (SqlException se) {
1058: throw se.getSQLException();
1059: }
1060: }
1061:
1062: //--------------------------JDBC 3.0-----------------------------
1063:
1064: public boolean getMoreResults(int current) throws SQLException {
1065: try {
1066: synchronized (connection_) {
1067: if (agent_.loggingEnabled()) {
1068: agent_.logWriter_.traceEntry(this ,
1069: "getMoreResults", current);
1070: }
1071: boolean resultIsResultSet = getMoreResultsX(current);
1072: if (agent_.loggingEnabled()) {
1073: agent_.logWriter_.traceExit(this , "getMoreResults",
1074: resultIsResultSet);
1075: }
1076: return resultIsResultSet;
1077: }
1078: } catch (SqlException se) {
1079: throw se.getSQLException();
1080: }
1081: }
1082:
1083: private boolean getMoreResultsX(int current) throws SqlException {
1084: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
1085: boolean resultIsResultSet;
1086: updateCount_ = -1;
1087: if (resultSetList_ == null) {
1088: if (resultSet_ != null) {
1089: if (current != KEEP_CURRENT_RESULT) {
1090: resultSet_.closeX();
1091: }
1092: resultSet_ = null;
1093: }
1094: resultIsResultSet = false;
1095: } else {
1096: if (numInvisibleRS_ == 0 && current == CLOSE_CURRENT_RESULT
1097: && resultSetList_[indexOfCurrentResultSet_] != null) {
1098: resultSetList_[indexOfCurrentResultSet_].closeX();
1099: }
1100: resultIsResultSet = indexOfCurrentResultSet_ + 1 < resultSetList_.length;
1101: }
1102: if ((current == CLOSE_ALL_RESULTS) && (numInvisibleRS_ == 0)) {
1103: int numberOfResultSetsToClose = (resultSetList_ == null) ? 0
1104: : indexOfCurrentResultSet_ + 1;
1105: boolean willTickleServer = willTickleServer(
1106: numberOfResultSetsToClose, false);
1107: if (willTickleServer) {
1108: flowCloseRetrievedResultSets();
1109: } else {
1110: flowCloseRetrievedResultSetsOutsideUOW();
1111: }
1112: }
1113: if (resultIsResultSet) {
1114: resultSet_ = resultSetList_[++indexOfCurrentResultSet_];
1115: } else {
1116: resultSet_ = null;
1117: }
1118:
1119: return resultIsResultSet;
1120: }
1121:
1122: public java.sql.ResultSet getGeneratedKeys() throws SQLException {
1123: try {
1124: if (agent_.loggingEnabled()) {
1125: agent_.logWriter_.traceEntry(this , "getGeneratedKeys");
1126: }
1127: checkForClosedStatement();
1128: if (agent_.loggingEnabled()) {
1129: agent_.logWriter_.traceExit(this , "getGeneratedKeys",
1130: generatedKeysResultSet_);
1131: }
1132: return generatedKeysResultSet_;
1133: } catch (SqlException se) {
1134: throw se.getSQLException();
1135: }
1136: }
1137:
1138: public int executeUpdate(String sql, int autoGeneratedKeys)
1139: throws SQLException {
1140: try {
1141: synchronized (connection_) {
1142: if (agent_.loggingEnabled()) {
1143: agent_.logWriter_.traceEntry(this , "executeUpdate",
1144: sql, autoGeneratedKeys);
1145: }
1146: autoGeneratedKeys_ = autoGeneratedKeys;
1147: int updateValue = executeUpdateX(sql);
1148: if (agent_.loggingEnabled()) {
1149: agent_.logWriter_.traceExit(this , "executeUpdate",
1150: updateValue);
1151: }
1152: return updateValue;
1153: }
1154: } catch (SqlException se) {
1155: throw se.getSQLException();
1156: }
1157: }
1158:
1159: public int executeUpdate(String sql, int columnIndexes[])
1160: throws SQLException {
1161: try {
1162: if (agent_.loggingEnabled()) {
1163: agent_.logWriter_.traceEntry(this , "executeUpdate",
1164: sql, columnIndexes);
1165: }
1166: checkForClosedStatement();
1167: throw new SqlException(agent_.logWriter_,
1168: new ClientMessageId(SQLState.NOT_IMPLEMENTED),
1169: "executeUpdate(String, int[])");
1170: } catch (SqlException se) {
1171: throw se.getSQLException();
1172: }
1173: }
1174:
1175: public int executeUpdate(String sql, String columnNames[])
1176: throws SQLException {
1177: try {
1178: synchronized (connection_) {
1179: if (agent_.loggingEnabled()) {
1180: agent_.logWriter_.traceEntry(this , "executeUpdate",
1181: sql, columnNames);
1182: }
1183: generatedKeysColumnNames_ = columnNames;
1184: int updateValue = executeUpdateX(sql);
1185: if (agent_.loggingEnabled()) {
1186: agent_.logWriter_.traceExit(this , "executeUpdate",
1187: updateValue);
1188: }
1189: return updateValue;
1190: }
1191: } catch (SqlException se) {
1192: throw se.getSQLException();
1193: }
1194: }
1195:
1196: public boolean execute(String sql, int autoGeneratedKeys)
1197: throws SQLException {
1198: try {
1199: synchronized (connection_) {
1200: if (agent_.loggingEnabled()) {
1201: agent_.logWriter_.traceEntry(this , "execute", sql,
1202: autoGeneratedKeys);
1203: }
1204: autoGeneratedKeys_ = autoGeneratedKeys;
1205: boolean b = executeX(sql);
1206: if (agent_.loggingEnabled()) {
1207: agent_.logWriter_.traceExit(this , "execute", b);
1208: }
1209: return b;
1210: }
1211: } catch (SqlException se) {
1212: throw se.getSQLException();
1213: }
1214: }
1215:
1216: public boolean execute(String sql, int columnIndexes[])
1217: throws SQLException {
1218: try {
1219: if (agent_.loggingEnabled()) {
1220: agent_.logWriter_.traceEntry(this , "execute", sql,
1221: columnIndexes);
1222: }
1223: checkForClosedStatement();
1224: throw new SqlException(agent_.logWriter_,
1225: new ClientMessageId(SQLState.NOT_IMPLEMENTED),
1226: "execute(String, int[])");
1227: } catch (SqlException se) {
1228: throw se.getSQLException();
1229: }
1230: }
1231:
1232: public boolean execute(String sql, String columnNames[])
1233: throws SQLException {
1234: try {
1235: synchronized (connection_) {
1236: if (agent_.loggingEnabled()) {
1237: agent_.logWriter_.traceEntry(this , "execute", sql,
1238: columnNames);
1239: }
1240: generatedKeysColumnNames_ = columnNames;
1241: boolean b = executeX(sql);
1242: if (agent_.loggingEnabled()) {
1243: agent_.logWriter_.traceExit(this , "execute", b);
1244: }
1245: return b;
1246: }
1247: } catch (SqlException se) {
1248: throw se.getSQLException();
1249: }
1250: }
1251:
1252: public int getResultSetHoldability() throws SQLException {
1253: try {
1254: if (agent_.loggingEnabled()) {
1255: agent_.logWriter_.traceEntry(this ,
1256: "getResultSetHoldability");
1257: }
1258: checkForClosedStatement();
1259: return resultSetHoldability_;
1260: } catch (SqlException se) {
1261: throw se.getSQLException();
1262: }
1263: }
1264:
1265: // ----------------------- box car and callback methods ---------------------
1266: // All callbacks must be client-side only operations.
1267: // Use of MaterialStatement interface is necessary to avoid multiple inheritance problem in Java.
1268: public void writeSetSpecialRegister(java.util.ArrayList sqlsttList)
1269: throws SqlException {
1270: materialStatement_.writeSetSpecialRegister_(sqlsttList);
1271: }
1272:
1273: public void readSetSpecialRegister() throws SqlException {
1274: materialStatement_.readSetSpecialRegister_();
1275: }
1276:
1277: public void writeExecuteImmediate(String sql, Section section)
1278: throws SqlException {
1279: materialStatement_.writeExecuteImmediate_(sql, section);
1280: }
1281:
1282: public void readExecuteImmediate() throws SqlException {
1283: materialStatement_.readExecuteImmediate_();
1284: }
1285:
1286: public void completeExecuteImmediate(Sqlca sqlca) {
1287: int sqlcode = completeSqlca(sqlca);
1288: if (sqlcode < 0) {
1289: return;
1290: }
1291: if (sqlca != null) {
1292: updateCount_ = sqlca.getUpdateCount();
1293: }
1294: }
1295:
1296: public void readExecuteImmediateForBatch(String sql)
1297: throws SqlException {
1298: materialStatement_.readExecuteImmediateForBatch_(sql);
1299: }
1300:
1301: public void writePrepareDescribeOutput(String sql, Section section)
1302: throws SqlException {
1303: materialStatement_.writePrepareDescribeOutput_(sql, section);
1304: }
1305:
1306: public void readPrepareDescribeOutput() throws SqlException {
1307: materialStatement_.readPrepareDescribeOutput_();
1308: }
1309:
1310: public void completePrepareDescribeOutput(
1311: ColumnMetaData resultSetMetaData, Sqlca sqlca) {
1312: completePrepare(sqlca);
1313: resultSetMetaData_ = resultSetMetaData;
1314: if (agent_.loggingEnabled()) {
1315: agent_.logWriter_.traceResultSetMetaData(this ,
1316: resultSetMetaData_);
1317: }
1318: }
1319:
1320: // Used for re-prepares across commit only
1321: public void writePrepare(String sql, Section section)
1322: throws SqlException {
1323: materialStatement_.writePrepare_(sql, section);
1324: }
1325:
1326: public void readPrepare() throws SqlException {
1327: materialStatement_.readPrepare_();
1328: }
1329:
1330: public void completePrepare(Sqlca sqlca) {
1331: int sqlcode = completeSqlca(sqlca);
1332: if (sqlcode < 0) {
1333: return;
1334: }
1335: markPrepared();
1336: }
1337:
1338: public void writeOpenQuery(Section section, int fetchSize,
1339: int resultSetType) throws SqlException {
1340: materialStatement_.writeOpenQuery_(section, fetchSize,
1341: resultSetType);
1342: }
1343:
1344: public void readOpenQuery() throws SqlException {
1345: materialStatement_.readOpenQuery_();
1346: }
1347:
1348: public void completeOpenQuery(Sqlca sqlca, ResultSet resultSet) {
1349: completeSqlca(sqlca);
1350: resultSet_ = resultSet;
1351: // For NET, resultSet_ == null when open query fails and receives OPNQFLRM.
1352: // Then, in NetStatementReply.parseOpenQueryFailure(), completeOpenQuery() is
1353: // invoked with resultSet explicitly set to null.
1354: if (resultSet == null) {
1355: return;
1356: }
1357: resultSet.resultSetMetaData_ = resultSetMetaData_;
1358: resultSet.resultSetMetaData_.resultSetConcurrency_ = resultSet.resultSetConcurrency_;
1359:
1360: // only cache the Cursor object for a PreparedStatement and if a Cursor object is
1361: // not already cached.
1362: if (cachedCursor_ == null && isPreparedStatement_) {
1363: cachedCursor_ = resultSet_.cursor_;
1364: }
1365:
1366: // The following two assignments should have already happened via prepareEvent(),
1367: // but are included here for safety for the time being.
1368: if (sqlca != null && sqlca.getSqlCode() < 0) {
1369: return;
1370: }
1371: openOnServer_ = true;
1372: resultSet.cursor_.rowsRead_ = 0;
1373:
1374: // Set fetchSize_ to the default(64) if not set by the user if the resultset is scrollable.
1375: // This fetchSize_ is used to check for a complete rowset when rowsets are parsed.
1376: // For scrollable cursors when the fetchSize_ is not set, (fetchSize_ == 0), a default
1377: // fetchSize of 64 is sent on behalf of the application, so we need to update the fetchSize_
1378: // here to 64.
1379: if (resultSet_.fetchSize_ == 0
1380: && (resultSet_.resultSetType_ == java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE || resultSet_.resultSetType_ == java.sql.ResultSet.TYPE_SCROLL_SENSITIVE)) {
1381: resultSet_
1382: .setFetchSize_(org.apache.derby.client.am.Configuration.defaultFetchSize);
1383: }
1384: }
1385:
1386: public void completeExecuteCallOpenQuery(Sqlca sqlca,
1387: ResultSet resultSet, ColumnMetaData resultSetMetaData,
1388: Section generatedSection) {
1389: resultSet.completeSqlca(sqlca);
1390: // For CallableStatements we can't just clobber the resultSet_ here, must use setResultSetEvent() separately
1391: resultSet.resultSetMetaData_ = resultSetMetaData;
1392:
1393: // The following two assignments should have already happened via prepareEvent(),
1394: // but are included here for safety for the time being.
1395: if (sqlca != null && sqlca.getSqlCode() < 0) {
1396: return;
1397: }
1398: openOnServer_ = true;
1399: resultSet.cursor_.rowsRead_ = 0;
1400:
1401: resultSet.generatedSection_ = generatedSection;
1402:
1403: // We are always sending the default fetchSize of 64 if not set for stored procedure calls.
1404: // This is different from the "normal" cursor case for forward_only cursors, where if
1405: // fetchSize_ is not set, we do not send any default value. Here since we always send
1406: // the fetchSize_, we need to set it to what we sent.
1407: if (resultSet.fetchSize_ == 0) {
1408: resultSet.fetchSize_ = org.apache.derby.client.am.Configuration.defaultFetchSize;
1409: }
1410: }
1411:
1412: public void writeExecuteCall(boolean outputExpected,
1413: String procedureName, Section section,
1414: int fetchSize,
1415: boolean suppressResultSets, // for batch updates == true
1416: int resultSetType, ColumnMetaData parameterMetaData,
1417: Object[] inputs) throws SqlException {
1418: materialStatement_.writeExecuteCall_(outputExpected,
1419: procedureName, section, fetchSize, suppressResultSets,
1420: resultSetType, parameterMetaData, inputs);
1421: }
1422:
1423: public void readExecuteCall() throws SqlException {
1424: materialStatement_.readExecuteCall_();
1425: }
1426:
1427: public void completeExecuteCall(Sqlca sqlca,
1428: Cursor singletonParams, ResultSet[] resultSets) {
1429: completeExecuteCall(sqlca, singletonParams);
1430: resultSetList_ = resultSets;
1431: if (resultSets != null) {
1432: resultSet_ = resultSets[0];
1433: }
1434: indexOfCurrentResultSet_ = 0;
1435: }
1436:
1437: public void completeExecuteCall(Sqlca sqlca, Cursor singletonParams) // no result sets returned
1438: {
1439: completeExecute(sqlca);
1440: //if ((sqlca != null) && ((sqlca.getSqlCode() < 0) || (sqlca.getSqlCode() == 100)))
1441: if (sqlca != null && sqlca.getSqlCode() < 0) {
1442: singletonRowData_ = null;
1443: } else {
1444: singletonRowData_ = singletonParams;
1445: if (cachedSingletonRowData_ == null && isPreparedStatement_) {
1446: cachedSingletonRowData_ = singletonRowData_;
1447: }
1448: }
1449: }
1450:
1451: // Callback for CALLS, and PreparedStatement updates.
1452: public void completeExecute(Sqlca sqlca) {
1453: if (sqlca == null) {
1454: return;
1455: }
1456:
1457: int sqlcode = sqlca.getSqlCode();
1458: if (sqlcode < 0) {
1459: agent_.accumulateReadException(new SqlException(
1460: agent_.logWriter_, sqlca));
1461: returnValueFromProcedure_ = sqlcode;
1462: } else {
1463: updateCount_ = sqlca.getUpdateCount();
1464: // sometime for call statement, protocol will return updateCount_, we will always set that to 0
1465: // sqlMode_ is not set for statements, only for prepared statements
1466: if (sqlMode_ == isCall__) {
1467: updateCount_ = -1;
1468: returnValueFromProcedure_ = sqlca.getSqlErrd()[0]; ////what is this for??
1469: }
1470: // Sqlcode 466 indicates a call statement has issued and result sets returned.
1471: // This is a good place to set some state variable to indicate result sets are open
1472: // for call, so that when autocommit is true, commit will not be issued until the
1473: // result sets are closed.
1474: // Currently, commit is not issued even there is no result set.
1475: // do not externalize sqlcode +100
1476: if (sqlcode > 0 && sqlcode != 466 && sqlcode != 100) {
1477: accumulateWarning(new SqlWarning(agent_.logWriter_,
1478: sqlca));
1479: }
1480: }
1481: }
1482:
1483: public void setUpdateCount(int updateCount) {
1484: updateCount_ = updateCount;
1485: }
1486:
1487: private boolean willTickleServer(int number,
1488: boolean allowAutoCommits) throws SqlException {
1489: boolean requiresAutocommit = false;
1490: if (resultSetList_ != null) {
1491: for (int i = 0; i < number; i++) {
1492: if (resultSetList_[i] != null) {
1493: if (resultSetList_[i].openOnServer_) {
1494: return true; // for the writeClose flow
1495: }
1496: if (!resultSetList_[i].autoCommitted_
1497: && allowAutoCommits) {
1498: requiresAutocommit = true; // for the commit flow
1499: }
1500: }
1501: }
1502: } else if (generatedKeysResultSet_ != null
1503: && generatedKeysResultSet_.openOnServer_) {
1504: generatedKeysResultSet_.writeClose();
1505: } else if (resultSet_ != null) {
1506: if (resultSet_.openOnServer_) {
1507: return true; // for the writeClose flow
1508: }
1509: if (!resultSet_.autoCommitted_ && allowAutoCommits) {
1510: requiresAutocommit = true;
1511: }
1512: }
1513: if (connection_.autoCommit_ && requiresAutocommit) { // for the auto-commit;
1514: if (connection_.isXAConnection_) {
1515: return (connection_.getXAState() == Connection.XA_T0_NOT_ASSOCIATED);
1516: } else {
1517: return true;
1518: }
1519: }
1520: return false;
1521: }
1522:
1523: private void flowClose() throws SqlException {
1524: agent_.beginWriteChain(this );
1525: writeClose(true); // true means permit auto-commits
1526: agent_.flow(this );
1527: readClose(true); // true means permit auto-commits
1528: agent_.endReadChain();
1529: }
1530:
1531: private void flowCloseOutsideUOW() throws SqlException {
1532: agent_.beginWriteChainOutsideUOW();
1533: writeClose(true); // true means permit auto-commits
1534: agent_.flowOutsideUOW();
1535: readClose(true); // true means permit auto-commits
1536: agent_.endReadChain();
1537: }
1538:
1539: final void writeClose(boolean allowAutoCommits) throws SqlException {
1540: writeCloseResultSets(allowAutoCommits);
1541: }
1542:
1543: final void readClose(boolean allowAutoCommits) throws SqlException {
1544: readCloseResultSets(allowAutoCommits);
1545: }
1546:
1547: boolean writeCloseResultSets(boolean allowAutoCommits)
1548: throws SqlException {
1549: int numberOfResultSetsToClose = (resultSetList_ == null) ? 0
1550: : resultSetList_.length;
1551: return writeCloseResultSets(numberOfResultSetsToClose,
1552: allowAutoCommits);
1553: }
1554:
1555: // The connection close processing passes allowAutoCommits=false because if we drove an
1556: // autocommits after each statement close, then when we issue close requests on non-held cursors
1557: // the server would complain that the non-held cursor was already closed from the previous statement's auto-commit.
1558: // So the solution is to never autocommit statements during connection close processing.
1559: //
1560: // Here's the operative explanation:
1561: // Given a sequence of open statements S1, S2, .... a logic problem is occuring after S1 close-query
1562: // drives an auto-commit, and S2 close-query is driven against a non-held cursor.
1563: // The first auto-commit driven by S1 triggers a callback that closes S2's non-held cursor,
1564: // and so the subsequent S2 close-query request generates an error from the server saying
1565: // that the cursor is already closed.
1566: //
1567: // This is fixed by passing a flag to our statement close processing that prevents
1568: // driving additional auto-commits after each statement close.
1569: // Connectino close drives its own final auto-commit.
1570: //
1571: boolean writeCloseResultSets(int number, boolean allowAutoCommits)
1572: throws SqlException {
1573: boolean requiresAutocommit = false;
1574: if (resultSetList_ != null) {
1575: for (int i = 0; i < number; i++) {
1576: if (resultSetList_[i] != null) {
1577: if (resultSetList_[i].openOnServer_) {
1578: resultSetList_[i].writeClose();
1579: }
1580: if (!resultSetList_[i].autoCommitted_
1581: && allowAutoCommits) {
1582: requiresAutocommit = true;
1583: }
1584: }
1585: }
1586: } else if (generatedKeysResultSet_ != null
1587: && generatedKeysResultSet_.openOnServer_) {
1588: generatedKeysResultSet_.writeClose();
1589: } else if (resultSet_ != null) {
1590: if (resultSet_.openOnServer_) {
1591: resultSet_.writeClose();
1592: }
1593: if (!resultSet_.autoCommitted_ && allowAutoCommits) {
1594: requiresAutocommit = true;
1595: }
1596: }
1597: if (connection_.autoCommit_ && requiresAutocommit
1598: && isAutoCommittableStatement_) {
1599: connection_.writeAutoCommit();
1600: if (connection_.isXAConnection_) {
1601: return (connection_.getXAState() == Connection.XA_T0_NOT_ASSOCIATED);
1602: } else {
1603: return true;
1604: }
1605: }
1606: return false;
1607: }
1608:
1609: // Helper method for S.flowCloseResultSets() and PS.flowExecute()
1610: void readCloseResultSets(boolean allowAutoCommits)
1611: throws SqlException {
1612: int numberOfResultSetsToClose = (resultSetList_ == null) ? 0
1613: : resultSetList_.length;
1614: readCloseResultSets(numberOfResultSetsToClose, allowAutoCommits);
1615: }
1616:
1617: void readCloseResultSets(int number, boolean allowAutoCommits)
1618: throws SqlException {
1619: boolean requiredAutocommit = false;
1620: if (resultSetList_ != null) {
1621: for (int i = 0; i < number; i++) {
1622: if (resultSetList_[i] != null) {
1623: if (resultSetList_[i].openOnServer_) {
1624: resultSetList_[i].readClose();
1625: } else {
1626: resultSetList_[i].markClosed();
1627: }
1628: if (!resultSetList_[i].autoCommitted_
1629: && allowAutoCommits) {
1630: requiredAutocommit = true;
1631: }
1632: }
1633: }
1634: } else if (generatedKeysResultSet_ != null) {
1635: if (generatedKeysResultSet_.openOnServer_) {
1636: generatedKeysResultSet_.readClose();
1637: } else {
1638: generatedKeysResultSet_.markClosed();
1639: }
1640: } else if (resultSet_ != null) {
1641: if (resultSet_.openOnServer_) {
1642: resultSet_.readClose();
1643: } else {
1644: resultSet_.markClosed();
1645: }
1646: if (!resultSet_.autoCommitted_ && allowAutoCommits) {
1647: requiredAutocommit = true;
1648: }
1649: }
1650: // we only commit when auto commit is turned on and at least one result set needed closing on server.
1651: if (connection_.autoCommit_ && requiredAutocommit
1652: && isAutoCommittableStatement_) {
1653: connection_.readAutoCommit();
1654: }
1655: }
1656:
1657: private void flowCloseRetrievedResultSets() throws SqlException {
1658: int numberOfResultSetsToClose = (resultSetList_ == null) ? 0
1659: : indexOfCurrentResultSet_ + 1;
1660: agent_.beginWriteChain(this );
1661: // Need to refactor the ResultSet.readClose() path to check if we are the
1662: // last result set closed in a set of multiple result sets of the owning statement,
1663: // if so, we need to flow the auto-commit (but only then).
1664: // currently, the code to do this is only in the closeX() path, which isn't called here
1665: writeCloseResultSets(numberOfResultSetsToClose, false);
1666: agent_.flow(this );
1667: readCloseResultSets(numberOfResultSetsToClose, false); // true means permit auto-commits
1668: agent_.endReadChain();
1669: }
1670:
1671: private void flowCloseRetrievedResultSetsOutsideUOW()
1672: throws SqlException {
1673: int numberOfResultSetsToClose = (resultSetList_ == null) ? 0
1674: : indexOfCurrentResultSet_ + 1;
1675: agent_.beginWriteChainOutsideUOW();
1676: // Need to refactor the ResultSet.readClose() path to check if we are the
1677: // last result set closed in a set of multiple result sets of the owning statement,
1678: // if so, we need to flow the auto-commit (but only then).
1679: // currently, the code to do this is only in the closeX() path, which isn't called here
1680: writeCloseResultSets(numberOfResultSetsToClose, false);
1681: agent_.flowOutsideUOW();
1682: readCloseResultSets(numberOfResultSetsToClose, false); // true means permit auto-commits
1683: agent_.endReadChain();
1684: }
1685:
1686: public int completeSqlca(Sqlca sqlca) {
1687: if (sqlca == null) {
1688: return 0;
1689: }
1690: int sqlcode = sqlca.getSqlCode();
1691: if (sqlcode < 0) {
1692: connection_.agent_
1693: .accumulateReadException(new SqlException(
1694: agent_.logWriter_, sqlca));
1695: } else if (sqlcode > 0) {
1696: accumulateWarning(new SqlWarning(agent_.logWriter_, sqlca));
1697: }
1698: return sqlcode;
1699: }
1700:
1701: public void completeExecuteSetStatement(Sqlca sqlca) {
1702: }
1703:
1704: void markClosedOnServer() {
1705: if (section_ != null) {
1706: section_.free();
1707: section_ = null;
1708: }
1709: openOnServer_ = false;
1710: // if an error occurs during the middle of the reset, before the statement
1711: // has a chance to reset its materialStatement_, and Agent.disconnectEvent() is called,
1712: // then the materialStatement_ here can be null.
1713: if (materialStatement_ != null) {
1714: materialStatement_.markClosedOnServer_();
1715: }
1716: }
1717:
1718: /**
1719: * This method cleans up client-side resources held by this Statement.
1720: * The Statement will not be removed from the open statements list and
1721: * PreparedStatement will also not be removed from the commit and rollback
1722: * listeners list in <code>org.apache.derby.client.am.Connection</code>.
1723: *
1724: * This method is called from:
1725: * 1. finalize() - For the finaizer to be called, the Statement
1726: * should not have any references and so it should have been already
1727: * removed from the lists.
1728: *
1729: * 2. <code>org.apache.derby.client.am.Connection#markStatementsClosed</code>
1730: * This method explicitly removes the Statement from open statements list.
1731: *
1732: * 3. To close positioned update statements - These statements are not
1733: * added to the list of open statements.
1734: */
1735: void markClosed() {
1736: markClosed(false);
1737: }
1738:
1739: /**
1740: * This method cleans up client-side resources held by this Statement.
1741: * If removeListener is true, the Statement is removed from open statements
1742: * list and PreparedStatement is also removed from commit and rollback
1743: * listeners list. This is called from the close methods.
1744: *
1745: * @param removeListener if true the Statement will be removed
1746: * from the open statements list and PreparedStatement will also be removed
1747: * from commit and rollback listeners list in
1748: * <code>org.apache.derby.client.am.Connection</code>.
1749: */
1750: void markClosed(boolean removeListener) {
1751: openOnClient_ = false;
1752: markResultSetsClosed();
1753: // in case a cursorName was set on the Statement but the Statement was
1754: // never used to execute a query, the cursorName will not be removed
1755: // when the resultSets are mark closed, so we need to remove the
1756: // cursorName form the cache.
1757: removeClientCursorNameFromCache();
1758: markPreparedStatementForAutoGeneratedKeysClosed();
1759: markClosedOnServer();
1760:
1761: // mark close ResultSetMetaData
1762: if (resultSetMetaData_ != null) {
1763: resultSetMetaData_.markClosed();
1764: resultSetMetaData_ = null;
1765: }
1766:
1767: if (removeListener)
1768: connection_.openStatements_.remove(this );
1769: }
1770:
1771: void markPreparedStatementForAutoGeneratedKeysClosed() {
1772: if (preparedStatementForAutoGeneratedKeys_ != null) {
1773: preparedStatementForAutoGeneratedKeys_.markClosed();
1774: }
1775: }
1776:
1777: /**
1778: * Mark all ResultSets associated with this statement as
1779: * closed. The ResultSets will not be removed from the commit and
1780: * rollback listeners list in
1781: * <code>org.apache.derby.client.am.Connection</code>.
1782: */
1783: void markResultSetsClosed() {
1784: markResultSetsClosed(false);
1785: }
1786:
1787: /**
1788: * Mark all ResultSets associated with this statement as
1789: * closed.
1790: *
1791: * @param removeListener if true the ResultSets will be removed
1792: * from the commit and rollback listeners list in
1793: * <code>org.apache.derby.client.am.Connection</code>.
1794: */
1795: void markResultSetsClosed(boolean removeListener) {
1796: if (resultSetList_ != null) {
1797: for (int i = 0; i < resultSetList_.length; i++) {
1798: if (resultSetList_[i] != null) {
1799: resultSetList_[i].markClosed(removeListener);
1800: }
1801: resultSetList_[i] = null;
1802: }
1803: }
1804: if (generatedKeysResultSet_ != null) {
1805: generatedKeysResultSet_.markClosed(removeListener);
1806: }
1807: if (resultSet_ != null) {
1808: resultSet_.markClosed(removeListener);
1809: }
1810: resultSet_ = null;
1811: resultSetList_ = null;
1812: generatedKeysResultSet_ = null;
1813: }
1814:
1815: private void flowExecute(int executeType, String sql)
1816: throws SqlException {
1817: checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
1818: checkAutoGeneratedKeysParameters();
1819: clearWarningsX(); // Per jdbc spec 0.7, and getWarnings() javadoc
1820:
1821: sql = escape(sql);
1822: parseSqlAndSetSqlModes(sql);
1823: if (sqlMode_ == isUpdate__) {
1824: updateCount_ = 0;
1825: } else {
1826: updateCount_ = -1;
1827: }
1828:
1829: checkForAppropriateSqlMode(executeType, sqlMode_);
1830:
1831: // DERBY-1036: Moved check till execute time to comply with embedded
1832: // behavior. Since we check here and not in setCursorName, several
1833: // statements can have the same cursor name as long as their result
1834: // sets are not simultaneously open.
1835:
1836: if (sqlMode_ == isQuery__) {
1837: checkForDuplicateCursorName();
1838: }
1839:
1840: boolean timeoutSent = false;
1841:
1842: agent_.beginWriteChain(this );
1843: boolean piggybackedAutoCommit = writeCloseResultSets(true); // true means permit auto-commits
1844:
1845: ResultSet scrollableRS = null;
1846: Section newSection = null;
1847: boolean repositionedCursor = false;
1848:
1849: // DERBY-1692: Statement objects need to send the timeout value for
1850: // each execution since the server will create a new statement
1851: // object each time. Since the server forgets the timeout value,
1852: // doWriteTimeout should not be reset, and it is OK not to send the
1853: // timeout value when it is zero.
1854: if (doWriteTimeout && (timeout_ > 0)) {
1855: timeoutArrayList.set(0, TIMEOUT_STATEMENT + timeout_);
1856: writeSetSpecialRegister(timeoutArrayList);
1857: timeoutSent = true;
1858: }
1859: switch (sqlMode_) {
1860: case isQuery__:
1861: newSection = agent_.sectionManager_
1862: .getDynamicSection(resultSetHoldability_);
1863:
1864: writePrepareDescribeOutput(sql, newSection);
1865: writeOpenQuery(newSection, fetchSize_, resultSetType_);
1866: break;
1867: case isUpdate__:
1868: String cursorName = null;
1869: if (sqlUpdateMode_ == isDeleteSql__
1870: || sqlUpdateMode_ == isUpdateSql__) {
1871: String[] sqlAndCursorName = extractCursorNameFromWhereCurrentOf(sql);
1872: if (sqlAndCursorName != null) {
1873: cursorName = sqlAndCursorName[0];
1874: sql = sqlAndCursorName[1];
1875: }
1876: }
1877: if (cursorName != null) {
1878: newSection = agent_.sectionManager_
1879: .getPositionedUpdateSection(cursorName, true); // true means get an execute immediate section
1880: if (newSection == null) {
1881: throw new SqlException(agent_.logWriter_,
1882: new ClientMessageId(
1883: SQLState.CURSOR_INVALID_NAME),
1884: cursorName);
1885: }
1886: scrollableRS = agent_.sectionManager_
1887: .getPositionedUpdateResultSet(cursorName);
1888: // do not need to reposition for rowset cursors
1889: if (scrollableRS != null
1890: && !scrollableRS.isRowsetCursor_) {
1891: repositionedCursor = scrollableRS
1892: .repositionScrollableResultSetBeforeJDBC1PositionedUpdateDelete();
1893: if (!repositionedCursor) {
1894: scrollableRS = null;
1895: }
1896: }
1897:
1898: // if client's cursor name is set, and the cursor name in the positioned update
1899: // string is the same as the client's cursor name, replace client's cursor name
1900: // with the server's cursor name.
1901: if (newSection.getClientCursorName() != null
1902: && cursorName.compareTo(newSection
1903: .getClientCursorName()) == 0) {
1904: // substitute cusor name in pass thru sql string
1905: sql = substituteClientCursorNameWithServerCursorName(
1906: sql, newSection);
1907: }
1908: writeExecuteImmediate(sql, newSection);
1909: }
1910: // if sql is an insert and columnNames is not null, and
1911: // then transform the insert statement into an
1912: // select from insert statement.
1913: // else chain an "select from identity_val_local()" to the insert statement
1914: else if (sqlUpdateMode_ == isInsertSql__
1915: && generatedKeysColumnNames_ != null) {
1916: newSection = agent_.sectionManager_
1917: .getDynamicSection(resultSetHoldability_);
1918: writePrepareDescribeOutput(
1919: constructSelectFromInsertSQL(sql), newSection);
1920: writeOpenQuery(newSection, fetchSize_, resultSetType_);
1921: } else {
1922: newSection = agent_.sectionManager_
1923: .getDynamicSection(resultSetHoldability_);
1924:
1925: writeExecuteImmediate(sql, newSection);
1926: if (sqlUpdateMode_ == isInsertSql__
1927: && autoGeneratedKeys_ == RETURN_GENERATED_KEYS) {
1928: prepareAutoGeneratedKeysStatement();
1929: writeOpenQuery(
1930: preparedStatementForAutoGeneratedKeys_.section_,
1931: preparedStatementForAutoGeneratedKeys_.fetchSize_,
1932: preparedStatementForAutoGeneratedKeys_.resultSetType_);
1933: }
1934: }
1935:
1936: // maybe duplicate a commit here if the sql is a "commit"
1937: if (connection_.autoCommit_) {
1938: connection_.writeAutoCommit();
1939: }
1940: break;
1941: case isCall__:
1942: newSection = writeExecuteCall(sql, false);
1943:
1944: break;
1945: }
1946:
1947: agent_.flow(this );
1948:
1949: readCloseResultSets(true); // true means permit auto-commits
1950:
1951: if (timeoutSent) {
1952: readSetSpecialRegister(); // Read response to the EXCSQLSET
1953: }
1954:
1955: // turn inUnitOfWork_ flag back on and add statement
1956: // back on commitListeners_ list if they were off
1957: // by an autocommit chained to a close cursor.
1958: if (piggybackedAutoCommit) {
1959: connection_.completeTransactionStart();
1960: }
1961:
1962: markResultSetsClosed(true); // true means remove from list of commit and rollback listeners
1963: markClosedOnServer();
1964: section_ = newSection;
1965:
1966: switch (sqlMode_) {
1967: case isQuery__:
1968: // parse out the reply to a chained prepare and open request
1969: readPrepareDescribeOutput();
1970: // This establishes statement.resultSet
1971: readOpenQuery();
1972:
1973: // resultSet_ is null if open query failed.
1974: // check for null resultSet_ before using it.
1975: // the first rowset comes back on OPEN for static non-rowset cursors.
1976: // no row is returned on open for rowset cursors.
1977: if (resultSet_ != null) {
1978: resultSet_.parseScrollableRowset();
1979:
1980: // DERBY-1183: If we set it up it earlier, the entry in
1981: // clientCursorNameCache_ gets wiped out by the closing of
1982: // result sets happening during readCloseResultSets above
1983: // because ResultSet#markClosed calls
1984: // Statement#removeClientCursorNameFromCache.
1985: setupCursorNameCacheAndMappings();
1986: }
1987:
1988: break;
1989:
1990: case isUpdate__:
1991:
1992: // do not need to reposition for rowset cursors.
1993: if (scrollableRS != null && !scrollableRS.isRowsetCursor_) {
1994: scrollableRS.readPositioningFetch_();
1995: }
1996:
1997: if (sqlUpdateMode_ == isInsertSql__
1998: && generatedKeysColumnNames_ != null) {
1999: readPrepareDescribeOutput();
2000: readOpenQuery();
2001: if (resultSet_ != null) {
2002: generatedKeysResultSet_ = resultSet_;
2003: resultSet_ = null;
2004: updateCount_ = 1;
2005: }
2006: } else {
2007: readExecuteImmediate();
2008:
2009: if (sqlUpdateMode_ == isInsertSql__
2010: && autoGeneratedKeys_ == RETURN_GENERATED_KEYS) {
2011: readPrepareAutoGeneratedKeysStatement();
2012: preparedStatementForAutoGeneratedKeys_
2013: .readOpenQuery();
2014: generatedKeysResultSet_ = preparedStatementForAutoGeneratedKeys_.resultSet_;
2015: preparedStatementForAutoGeneratedKeys_.resultSet_ = null;
2016: }
2017: }
2018:
2019: if (connection_.autoCommit_) {
2020: connection_.readAutoCommit();
2021: }
2022: break;
2023:
2024: case isCall__:
2025: readPrepare();
2026: readExecuteCall();
2027: break;
2028:
2029: }
2030:
2031: // in the case the stored procedure call is uncatalogued, we need to catch that
2032: // kind exception and changed the call from dynamic to static
2033: agent_.endReadChain();
2034:
2035: // If we hear from Sun that we can just set a warning for this, then move this code to the ResultSet constructor.
2036: // Throw an exception if holdability returned by the server is different from requested.
2037: if (resultSet_ != null
2038: && resultSet_.resultSetHoldability_ != resultSetHoldability_
2039: && sqlMode_ != isCall__) {
2040: throw new SqlException(
2041: agent_.logWriter_,
2042: new ClientMessageId(
2043: SQLState.UNABLE_TO_OPEN_RS_WITH_REQUESTED_HOLDABILITY),
2044: new Integer(resultSetHoldability_));
2045: }
2046:
2047: // In the case of executing a call to a stored procedure.
2048: if (sqlMode_ == isCall__) {
2049: parseStorProcReturnedScrollableRowset();
2050: checkForStoredProcResultSetCount(executeType);
2051: // When there is no result sets back, we will commit immediately when autocommit is true.
2052: if (connection_.autoCommit_ && resultSet_ == null
2053: && resultSetList_ == null) {
2054: connection_.flowAutoCommit();
2055: }
2056: }
2057:
2058: // The JDBC spec says that executeUpdate() should return 0
2059: // when no row count is returned.
2060: if (executeType == executeUpdateMethod__ && updateCount_ < 0) {
2061: updateCount_ = 0;
2062: }
2063: }
2064:
2065: void flowExecuteBatch(int[] updateCounts) throws SqlException,
2066: BatchUpdateException {
2067: SqlException chainBreaker = null;
2068: boolean isCallCataloguedBestGuess = true;
2069: agent_.beginBatchedWriteChain(this );
2070: for (int i = 0; i < batch_.size(); i++) {
2071: boolean flowSQL = true;
2072: String sql = (String) batch_.get(i);
2073: parseSqlAndSetSqlModes(sql);
2074: try {
2075: checkForInvalidBatchedSql(sql);
2076: } catch (SqlException e) {
2077: flowSQL = false;
2078: }
2079:
2080: // if you have a length mismatch for a lob flow, then we need to return a -3
2081: // need to trap the exceptions coming back from writeExecuteImmediate and continue on with a -3
2082: // net will need to be able to reset the send buffer
2083: if (flowSQL) {
2084: if (section_ != null) {
2085: section_.free();
2086: }
2087: if (sqlMode_ != isCall__) {
2088: section_ = agent_.sectionManager_
2089: .getDynamicSection(resultSetHoldability_);
2090: writeExecuteImmediate(sql, section_);
2091: } else {
2092: section_ = writeExecuteCall(sql, true);
2093: }
2094: }
2095: }
2096:
2097: if (connection_.autoCommit_) {
2098: connection_.writeAutoCommit();
2099: }
2100:
2101: agent_.flowBatch(this , batch_.size());
2102:
2103: try {
2104: for (int i = 0; i < batch_.size(); i++) {
2105: agent_.setBatchedExceptionLabelIndex(i);
2106: SqlException invalidSQLCaughtByClient = null;
2107: String sql = (String) batch_.get(i);
2108: parseSqlAndSetSqlModes(sql);
2109: try {
2110: checkForInvalidBatchedSql(sql);
2111: } catch (SqlException e) {
2112: invalidSQLCaughtByClient = e;
2113: }
2114: if (invalidSQLCaughtByClient == null) {
2115: updateCount_ = -1;
2116: if (sqlMode_ != isCall__) {
2117: readExecuteImmediateForBatch(sql);
2118: } else {
2119: if (isCallCataloguedBestGuess) {
2120: readPrepare();
2121: }
2122: readExecuteCall();
2123: }
2124: } else {
2125: agent_
2126: .accumulateReadException(invalidSQLCaughtByClient);
2127: updateCount_ = java.sql.Statement.EXECUTE_FAILED;
2128: invalidSQLCaughtByClient = null;
2129: }
2130:
2131: updateCounts[i] = updateCount_;
2132:
2133: // DERBY doesn't return an update count for DDL statements, so we need to
2134: // remap our initial value of -1 (represents invalid update count) to a
2135: // valid update count of zero.
2136: if (updateCounts[i] == -1) {
2137: updateCounts[i] = 0;
2138: }
2139: }
2140: agent_.disableBatchedExceptionTracking(); // to prvent the following readCommit() from getting a batch label
2141: if (connection_.autoCommit_) {
2142: connection_.readAutoCommit(); // this could throw a chainbreaker too
2143: }
2144: }
2145: // for chain-breaking exception only, all read() methods do their own accumulation
2146: // this catches the entire accumulated chain, we need to be careful not to
2147: // reaccumulate it on the agent since the batch labels will be overwritten if
2148: // batch exception tracking is enabled.
2149: catch (SqlException e) {
2150: chainBreaker = e;
2151: chainBreaker.setNextException(new SqlException(
2152: agent_.logWriter_, new ClientMessageId(
2153: SQLState.BATCH_CHAIN_BREAKING_EXCEPTION)));
2154: }
2155: // We need to clear the batch before any exception is thrown from agent_.endBatchedReadChain().
2156: batch_.clear();
2157: agent_.endBatchedReadChain(updateCounts, chainBreaker);
2158: }
2159:
2160: private Section writeExecuteCall(String sql, boolean isBatch)
2161: throws SqlException {
2162: Section newSection = null;
2163:
2164: newSection = agent_.sectionManager_
2165: .getDynamicSection(resultSetHoldability_);
2166: // this code is beneficial only if there is literal in the sql call statement
2167: writePrepare(sql, newSection);
2168: writeExecuteCall(false, // no out parameters, outputExpected = false
2169: null, // sql is prepared, procedureName = null
2170: newSection, fetchSize_, isBatch, // do not suppress ResultSets for regular CALLs
2171: resultSetType_, null, // no parameters, parameterMetaData = null
2172: null); // no parameters, inputs = null
2173:
2174: return newSection;
2175: }
2176:
2177: //------------------material layer event callbacks follow---------------------
2178: // All callbacks are client-side only operations
2179:
2180: public void listenToUnitOfWork() {
2181: } // do nothing for now.
2182:
2183: public void completeLocalCommit(java.util.Iterator listenerIterator) {
2184: } // do nothing for now.
2185:
2186: public void completeLocalRollback(
2187: java.util.Iterator listenerIterator) {
2188: } // do nothing for now.
2189:
2190: // This method will not work if e is chained.
2191: // It is assumed that e is a single warning and is not chained.
2192: public void accumulateWarning(SqlWarning e) {
2193: if (warnings_ == null) {
2194: warnings_ = e;
2195: } else {
2196: warnings_.setNextWarning(e);
2197: }
2198: }
2199:
2200: private void markPrepared() {
2201: //openOnClient_ = true;
2202: openOnServer_ = true;
2203: listenToUnitOfWork();
2204: }
2205:
2206: //-------------------------------helper methods-------------------------------
2207:
2208: /**
2209: * Returns the name of the java.sql interface implemented by this class.
2210: * @return name of java.sql interface
2211: */
2212: protected String getJdbcStatementInterfaceName() {
2213: return "java.sql.Statement";
2214: }
2215:
2216: // Should investigate if it can be optimized.. if we can avoid this parsing..
2217: //
2218: void parseSqlAndSetSqlModes(String sql) throws SqlException {
2219: String delims = "\t\n\r\f=? (";
2220: java.util.StringTokenizer tokenizer = null;
2221: String firstToken = null;
2222:
2223: // See if the statement starts with a comment; if so, move
2224: // past the comment and get the first token of the actual
2225: // statement to be executed. Note: must use "startsWith"
2226: // when looking for the comment delimiters instead of
2227: // "equals" because there may not be whitespace between the
2228: // the delimiter and the comment itself, ex "--my comment".
2229: if (sql.trim().startsWith("--")) {
2230:
2231: // Read each line of the statement until we find a
2232: // line that is NOT a comment.
2233: int lastEndLine = -1;
2234: String endline = "\n\r\f";
2235: tokenizer = new java.util.StringTokenizer(sql, endline,
2236: true);
2237: while (tokenizer.hasMoreTokens()) {
2238: firstToken = tokenizer.nextToken();
2239: if (endline.indexOf(firstToken) != -1)
2240: // this is some sort of newline ("\n", "\r", or "\f").
2241: lastEndLine = sql.indexOf(firstToken,
2242: lastEndLine + 1);
2243: else if (!firstToken.trim().startsWith("--"))
2244: break;
2245: }
2246:
2247: if (firstToken.startsWith("--")) {
2248: // entire statement was just one or more comments; pass it as
2249: // a query to the server and let the server deal with it.
2250: sqlMode_ = isQuery__;
2251: return;
2252: } else {
2253: // we have a non-comment line; get a tokenizer for the
2254: // statement beginning at the start of this line.
2255: tokenizer = new java.util.StringTokenizer(sql
2256: .substring(lastEndLine + 1), delims);
2257: }
2258:
2259: } else {
2260: // there aren't any leading comments, so just get the first token
2261: // in the SQL statement.
2262: tokenizer = new java.util.StringTokenizer(sql, delims);
2263: }
2264:
2265: if (!tokenizer.hasMoreTokens()) {
2266: throw new SqlException(
2267: agent_.logWriter_,
2268: new ClientMessageId(SQLState.NO_TOKENS_IN_SQL_TEXT),
2269: sql);
2270: }
2271:
2272: sqlUpdateMode_ = 0;
2273: firstToken = tokenizer.nextToken();
2274:
2275: if (firstToken.equalsIgnoreCase("select") || // captures <subselect> production
2276: firstToken.equalsIgnoreCase("values")) // captures <values-clause> production
2277: {
2278: sqlMode_ = isQuery__;
2279: } else if (firstToken.equalsIgnoreCase("call")) // captures CALL...and ?=CALL...
2280: {
2281: sqlMode_ = isCall__;
2282: } else {
2283: parseUpdateSql(firstToken);
2284: }
2285: }
2286:
2287: private void parseUpdateSql(String firstToken) throws SqlException {
2288: sqlMode_ = isUpdate__;
2289: if (firstToken.equalsIgnoreCase("insert")) {
2290: sqlUpdateMode_ = isInsertSql__;
2291: }
2292: if (firstToken.equalsIgnoreCase("delete")) {
2293: sqlUpdateMode_ = isDeleteSql__;
2294: }
2295: if (firstToken.equalsIgnoreCase("update")) {
2296: sqlUpdateMode_ = isUpdateSql__;
2297: }
2298: }
2299:
2300: // the sql is assumed to start with CALL... or ?=CALL...
2301: String getProcedureName(String sql) throws SqlException {
2302: java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
2303: sql, "\t\n\r\f= (?");
2304: if (!tokenizer.hasMoreTokens()) {
2305: throw new SqlException(
2306: agent_.logWriter_,
2307: new ClientMessageId(SQLState.NO_TOKENS_IN_SQL_TEXT),
2308: sql);
2309: }
2310: String firstToken = tokenizer.nextToken();
2311: if (!firstToken.equalsIgnoreCase("call")) {
2312: throw new SqlException(agent_.logWriter_,
2313: new ClientMessageId(
2314: SQLState.LANG_INVALID_CALL_STATEMENT));
2315: }
2316: if (!tokenizer.hasMoreTokens()) {
2317: throw new SqlException(agent_.logWriter_,
2318: new ClientMessageId(
2319: SQLState.LANG_INVALID_CALL_STATEMENT));
2320: }
2321: return tokenizer.nextToken();
2322: }
2323:
2324: // Try to enforce the use of this method later.
2325: public static String upperCaseProcedureName(String procedureName)
2326: throws SqlException {
2327: // upper case the parts of a 3-part procedure name unless the part is in a double quotes
2328:
2329: // Loop thru every character, if we're in double quotes just echo it,
2330: // if we're not in double quotes, upper case it.
2331: char[] charArray = null;
2332: if (procedureName.indexOf("\"") == -1) {
2333: return procedureName.toUpperCase();
2334: } else {
2335: charArray = procedureName.toCharArray();
2336: boolean inStringLiteral = false;
2337: for (int i = 0; i < charArray.length; i++) {
2338: if (charArray[i] == '"') {
2339: inStringLiteral = !inStringLiteral;
2340: } else if (!inStringLiteral && charArray[i] != '.') {
2341: charArray[i] = Character.toUpperCase(charArray[i]);
2342: }
2343: }
2344: }
2345: return new String(charArray);
2346: }
2347:
2348: void checkForAppropriateSqlMode(int executeType, int sqlMode)
2349: throws SqlException {
2350: if (executeType == executeQueryMethod__
2351: && sqlMode == isUpdate__) {
2352: throw new SqlException(agent_.logWriter_,
2353: new ClientMessageId(
2354: SQLState.CANT_USE_EXEC_QUERY_FOR_UPDATE));
2355: }
2356: if (executeType == executeUpdateMethod__
2357: && sqlMode == isQuery__) {
2358: throw new SqlException(
2359: agent_.logWriter_,
2360: new ClientMessageId(
2361: SQLState.LANG_INVALID_CALL_TO_EXECUTE_UPDATE));
2362: }
2363: }
2364:
2365: /**
2366: * Checks that the number of result sets returned by the statement
2367: * is consistent with the executed type. <code>executeQuery()</code>
2368: * should return exactly one result set and <code>executeUpdate()</code>
2369: * none. Raises an exception if the result set count does not match the
2370: * execute type.
2371: *
2372: * @param executeType one of <code>executeQueryMethod__</code>,
2373: * <code>executeUpdateMethod__</code> and <code>executeMethod__</code>
2374: * @exception SqlException if the number of result sets does not
2375: * match the execute type
2376: */
2377: private void checkResultSetCount(int executeType)
2378: throws SqlException {
2379: switch (executeType) {
2380: case executeQueryMethod__:
2381: // We'll just rely on finalizers to close the dangling result sets.
2382: if (resultSetList_ != null && resultSetList_.length > 1) {
2383: throw new SqlException(
2384: agent_.logWriter_,
2385: new ClientMessageId(
2386: SQLState.MULTIPLE_RESULTS_ON_EXECUTE_QUERY),
2387: getJdbcStatementInterfaceName(),
2388: getJdbcStatementInterfaceName());
2389: }
2390: if (resultSet_ == null || resultSetList_.length == 0) {
2391: ClientMessageId messageId = new ClientMessageId(
2392: SQLState.USE_EXECUTE_UPDATE_WITH_NO_RESULTS);
2393: throw new SqlException(agent_.logWriter_, messageId,
2394: getJdbcStatementInterfaceName(),
2395: getJdbcStatementInterfaceName());
2396: }
2397: break;
2398: case executeUpdateMethod__:
2399: // We'll just rely on finalizers to close the dangling result sets.
2400: if (resultSet_ != null && resultSetList_.length > 0) {
2401: ClientMessageId messageId = new ClientMessageId(
2402: SQLState.LANG_INVALID_CALL_TO_EXECUTE_UPDATE);
2403: throw new SqlException(agent_.logWriter_, messageId);
2404: }
2405: break;
2406: }
2407: }
2408:
2409: /**
2410: * Checks that a stored procedure returns the correct number of
2411: * result sets given its execute type. If the number is incorrect,
2412: * make sure the transaction is rolled back when auto commit is
2413: * enabled.
2414: *
2415: * @param executeType one of <code>executeQueryMethod__</code>,
2416: * <code>executeUpdateMethod__</code> and <code>executeMethod__</code>
2417: * @exception SqlException if the number of result sets does not
2418: * match the execute type
2419: * @see #checkResultSetCount(int)
2420: */
2421: protected final void checkForStoredProcResultSetCount(
2422: int executeType) throws SqlException {
2423: try {
2424: checkResultSetCount(executeType);
2425: } catch (SqlException se) {
2426: if (connection_.autoCommit_) {
2427: connection_.flowRollback();
2428: }
2429: throw se;
2430: }
2431: }
2432:
2433: void checkForClosedStatement() throws SqlException {
2434: // For some odd reason, there was a JVM hotspot error with Sun's 1.4 JDK
2435: // when the code was written like this:
2436: // agent_checkForDeferredExceptions();
2437: // if (!openOnClient_)
2438: // throw new SqlException (agent_.logWriter_, "Invalid operation: statement closed");
2439: //
2440: if (this .connection_ == null || this .connection_.isClosed())
2441: throw new SqlException(agent_.logWriter_,
2442: new ClientMessageId(SQLState.NO_CURRENT_CONNECTION));
2443:
2444: if (!openOnClient_) {
2445: agent_.checkForDeferredExceptions();
2446: throw new SqlException(agent_.logWriter_,
2447: new ClientMessageId(SQLState.ALREADY_CLOSED),
2448: "Statement");
2449: } else {
2450: agent_.checkForDeferredExceptions();
2451: }
2452: }
2453:
2454: // precondition: parseSqlAndSetSqlModes() must be called on the supplied sql string before invoking this method
2455: void checkForInvalidBatchedSql(String sql) throws SqlException {
2456: if (sql == null) {
2457: throw new SqlException(agent_.logWriter_,
2458: new ClientMessageId(SQLState.NULL_SQL_TEXT));
2459: }
2460:
2461: if (sqlMode_ != isCall__
2462: && !(sqlMode_ == isUpdate__ && (sqlUpdateMode_ == isInsertSql__
2463: || sqlUpdateMode_ == isDeleteSql__
2464: || sqlUpdateMode_ == isUpdateSql__ || sqlUpdateMode_ == 0)))// For any undefined pass thru statement like drop create
2465: {
2466: throw new SqlException(agent_.logWriter_,
2467: new ClientMessageId(
2468: SQLState.LANG_INVALID_SQL_IN_BATCH), sql);
2469: }
2470: }
2471:
2472: // Two open result sets can not have the same cursor name.
2473: protected void checkForDuplicateCursorName() throws SqlException {
2474: if (cursorName_ != null
2475: && (connection_.clientCursorNameCache_
2476: .containsKey(cursorName_))) {
2477: throw new SqlException(
2478: agent_.logWriter_,
2479: new ClientMessageId(SQLState.CURSOR_DUPLICATE_NAME),
2480: cursorName_);
2481: }
2482: }
2483:
2484: // Set up information to be able to handle cursor names:
2485: // canned or user named (via setCursorName).
2486: protected void setupCursorNameCacheAndMappings() {
2487: if (cursorName_ != null) {
2488: // The user has set a cursor name for this statement.
2489: // This means we must subtitute the <users-cursor-name>
2490: // with the <canned-cursor-name> in the pass-thru sql
2491: // string "...where current of <canned-cursor-name>"
2492: // whenever the result set produced by this statement
2493: // is referenced in a positioned update/delete statement.
2494: agent_.sectionManager_.mapCursorNameToQuerySection(
2495: cursorName_, section_);
2496: section_.setClientCursorName(cursorName_);
2497:
2498: // Update cache to avoid duplicates of user set cursor name.
2499: connection_.clientCursorNameCache_.put(cursorName_,
2500: cursorName_);
2501: } else {
2502: // canned cursor name
2503: agent_.sectionManager_.mapCursorNameToQuerySection(section_
2504: .getServerCursorName(), section_);
2505: }
2506:
2507: // If client's cursor name is set, map the client's cursor name to the
2508: // result set, else map the server's cursor name to the result set.
2509: mapCursorNameToResultSet();
2510: }
2511:
2512: String[] extractCursorNameFromWhereCurrentOf(String sql) {
2513: String lowerSql = sql.toLowerCase();
2514: int currentIndex = lowerSql.lastIndexOf("current");
2515: if (currentIndex != -1) {
2516: int whereIndex = lowerSql.lastIndexOf("where");
2517: if (whereIndex != -1) {
2518: String[] whereCurrentOf = { "where", "current", "of" };
2519: java.util.StringTokenizer st = new java.util.StringTokenizer(
2520: sql.substring(whereIndex));
2521: while (st.hasMoreTokens()) {
2522: if (st.nextToken().equalsIgnoreCase(
2523: whereCurrentOf[0])
2524: && st.nextToken().equalsIgnoreCase(
2525: whereCurrentOf[1])
2526: && st.nextToken().equalsIgnoreCase(
2527: whereCurrentOf[2])) {
2528: String cursorName = st.nextToken();
2529: String oldCursorName = cursorName;
2530: int originalCursorNameLength = cursorName
2531: .length();
2532: int index = sql.lastIndexOf(cursorName);
2533: if (cursorName.charAt(0) == '\"'
2534: && cursorName.charAt(cursorName
2535: .length() - 1) == '\"') {
2536: cursorName = cursorName.substring(1,
2537: cursorName.length() - 1);
2538: } else {
2539: cursorName = cursorName.toUpperCase();
2540: }
2541: // we cannot assume "where current of cursorName" is always the end of the sql string
2542: // with rowset cursors, it can be "where current of cursorName for row X of rowset"
2543: if (sql.length() > index
2544: + originalCursorNameLength) {
2545: sql = sql.substring(0, index)
2546: + cursorName
2547: + sql.substring(index
2548: + oldCursorName.length(),
2549: sql.length());
2550: } else {
2551: sql = sql.substring(0, index) + cursorName;
2552: }
2553: return new String[] { cursorName, sql }; // delimited name, so just extract the name.
2554: }
2555: }
2556: }
2557: }
2558: return null;
2559: }
2560:
2561: // Substitute the client cursor name in the SQL string with the server's cursor name.
2562: // Only called on positioned update statements.
2563: protected String substituteClientCursorNameWithServerCursorName(
2564: String sql, Section section) throws SqlException {
2565: String clientCursorName = section.getClientCursorName();
2566: int index = sql.lastIndexOf(clientCursorName);
2567: if (sql.length() > index + clientCursorName.length()) {
2568: return sql.substring(0, index)
2569: + section.getServerCursorNameForPositionedUpdate()
2570: + sql.substring(index + clientCursorName.length(),
2571: sql.length());
2572: } else {
2573: return sql.substring(0, index)
2574: + section.getServerCursorNameForPositionedUpdate();
2575: }
2576: }
2577:
2578: public ConnectionCallbackInterface getConnectionCallbackInterface() {
2579: return connection_;
2580: }
2581:
2582: // This was being called only on positioned update statements. When working
2583: // on DERBY-210, it was found that result sets of all statements (not just
2584: // positioned update statements) get added to the table. So, this is called
2585: // for all statements. Otherwise, this will cause memory leaks when statements
2586: // are not explicitly closed in the application.
2587: void resetCursorNameAndRemoveFromWhereCurrentOfMappings() {
2588: // Remove client/server cursorName -> ResultSet mapping from the hashtable.
2589: // If Statement.close() is called before ResultSet.close(), then statement_.section is null.
2590: if (section_ != null) {
2591: agent_.sectionManager_.removeCursorNameToResultSetMapping(
2592: cursorName_, section_
2593: .getServerCursorNameForPositionedUpdate());
2594:
2595: // remove resultset mapping for other cursors (other than positioned
2596: // update statements) - DERBY-210
2597: agent_.sectionManager_.removeCursorNameToResultSetMapping(
2598: cursorName_, section_.getServerCursorName());
2599:
2600: // Remove client and server cursorName -> QuerySection mapping from the hashtable
2601: // if one exists
2602: agent_.sectionManager_
2603: .removeCursorNameToQuerySectionMapping(
2604: cursorName_,
2605: section_
2606: .getServerCursorNameForPositionedUpdate());
2607: }
2608:
2609: // client cursor name will be set to null when it is removed from the
2610: // clientCursorNameCache.
2611: //cursorName_ = null;
2612: }
2613:
2614: void mapCursorNameToResultSet() {
2615: if (cursorName_ != null) {
2616: agent_.sectionManager_.mapCursorNameToResultSet(
2617: cursorName_, resultSet_);
2618: } else {
2619: agent_.sectionManager_.mapCursorNameToResultSet(section_
2620: .getServerCursorName(), resultSet_);
2621: }
2622: }
2623:
2624: void parseStorProcReturnedScrollableRowset() throws SqlException {
2625: if (resultSetList_ != null) {
2626: for (int i = 0; i < resultSetList_.length; i++) {
2627: if (resultSetList_[i].scrollable_
2628: && resultSetList_[i].cursor_
2629: .dataBufferHasUnprocessedData()) {
2630: resultSetList_[i].parseScrollableRowset();
2631: if (resultSetList_[i].rowCountIsUnknown()) {
2632: resultSetList_[i].getRowCount();
2633: }
2634: }
2635: }
2636: }
2637: }
2638:
2639: String escape(String sql) throws SqlException {
2640: String nativeSQL = sql;
2641:
2642: nativeSQL = connection_.nativeSQLX(sql);
2643: return nativeSQL;
2644: }
2645:
2646: // Called by statement constructor only for jdbc 2 statements with scroll attributes.
2647: // This method is not in the StatementRequest class because it is not building into
2648: // the request buffer directly, it is building into a cache to be written into the request
2649: // buffer at prepare-time.
2650: String cacheCursorAttributesToSendOnPrepare() throws SqlException {
2651: StringBuffer cursorAttributes = new StringBuffer();
2652: if (resultSetType_ == java.sql.ResultSet.TYPE_SCROLL_SENSITIVE) {
2653: // append "SENSITIVE STATIC SCROLL"
2654: cursorAttributes
2655: .append(Configuration.cursorAttribute_SensitiveStatic);
2656: } else if (resultSetType_ == java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE) {
2657: // if "insensitve, updatable" cursor is asked, then server sends back error
2658: // and we will pass that error back to the user.
2659: // we will not try to catch any errors/warnings here.
2660: // append "INSENSITIVE SCROLL"
2661: cursorAttributes
2662: .append(Configuration.cursorAttribute_Insensitive);
2663: }
2664:
2665: // Default is read-only, forward-only. No attribute needs to be sent.
2666: if (resultSetConcurrency_ == java.sql.ResultSet.CONCUR_UPDATABLE) {
2667: cursorAttributes
2668: .append(Configuration.cursorAttribute_ForUpdate); // FOR UPDATE
2669: }
2670:
2671: if ((resultSetHoldability_ == java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT)) {
2672: cursorAttributes
2673: .append(Configuration.cursorAttribute_WithHold); // WITH HOLD
2674: }
2675:
2676: return (cursorAttributes == null || cursorAttributes.toString()
2677: .equals("")) ? null : cursorAttributes.toString();
2678: }
2679:
2680: protected String constructSelectFromInsertSQL(String sql) {
2681: String temp = "select ";
2682: int numOfColumns = generatedKeysColumnNames_.length;
2683:
2684: for (int i = 0; i < numOfColumns; i++) {
2685: temp += generatedKeysColumnNames_[i];
2686: if ((i + 1) < numOfColumns) {
2687: temp += ",";
2688: }
2689: }
2690: temp += (" from final table (" + sql + ")");
2691: return temp;
2692: }
2693:
2694: void getPreparedStatementForAutoGeneratedKeys() throws SqlException {
2695: if (preparedStatementForAutoGeneratedKeys_ == null) {
2696: String s = "select IDENTITY_VAL_LOCAL() from SYSIBM.SYSDUMMY1";
2697: preparedStatementForAutoGeneratedKeys_ = connection_
2698: .newPreparedStatement_(
2699: s,
2700: java.sql.ResultSet.TYPE_FORWARD_ONLY,
2701: java.sql.ResultSet.CONCUR_READ_ONLY,
2702: java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT,
2703: java.sql.Statement.NO_GENERATED_KEYS, null);
2704: // need a special case for Derby, since the attribute has to go through the wire.
2705: // This same special casing for Derby is already in place in method PS.cacheCursorAttributesToSendOnPrepare() as called by prepareStatementX().
2706: // We need to figure how some way to get code reuse here, ie. to consolidate to just one special casing rather than two?
2707: // Maybe just call prepareStatementX() or use some special purpose prepare method like the ones in Connection.
2708: // Something more abstract so that we don't have to special case the WITH HOLD thing twice.
2709: StringBuffer cursorAttributes = new StringBuffer();
2710: cursorAttributes
2711: .append(Configuration.cursorAttribute_WithHold);
2712: preparedStatementForAutoGeneratedKeys_.cursorAttributesToSendOnPrepare_ = cursorAttributes
2713: .toString();
2714: }
2715: }
2716:
2717: void prepareAutoGeneratedKeysStatement() throws SqlException {
2718: getPreparedStatementForAutoGeneratedKeys();
2719: if (!preparedStatementForAutoGeneratedKeys_.openOnServer_) {
2720: preparedStatementForAutoGeneratedKeys_.materialPreparedStatement_
2721: .writePrepareDescribeOutput_(
2722: preparedStatementForAutoGeneratedKeys_.sql_,
2723: preparedStatementForAutoGeneratedKeys_.section_);
2724: }
2725: }
2726:
2727: void readPrepareAutoGeneratedKeysStatement() throws SqlException {
2728: if (!preparedStatementForAutoGeneratedKeys_.openOnServer_) {
2729: preparedStatementForAutoGeneratedKeys_.materialPreparedStatement_
2730: .readPrepareDescribeOutput_();
2731: }
2732: }
2733:
2734: void checkAutoGeneratedKeysParameters() throws SqlException {
2735: if (autoGeneratedKeys_ != java.sql.Statement.NO_GENERATED_KEYS
2736: && autoGeneratedKeys_ != java.sql.Statement.RETURN_GENERATED_KEYS) {
2737: throw new SqlException(
2738: agent_.logWriter_,
2739: new ClientMessageId(SQLState.INVALID_API_PARAMETER),
2740: new Integer(autoGeneratedKeys_),
2741: "autoGeneratedKeys",
2742: "Statement.execute()/executeQuery()");
2743: }
2744:
2745: if (generatedKeysColumnNames_ != null) {
2746: throw new SqlException(agent_.logWriter_,
2747: new ClientMessageId(SQLState.NOT_IMPLEMENTED),
2748: "Connection.prepareStatement(String sql, String[] columnNames)");
2749: }
2750: }
2751:
2752: public ColumnMetaData getGuessedResultSetMetaData() {
2753: return resultSetMetaData_;
2754: }
2755:
2756: public boolean isQueryMode() {
2757: if (this .sqlMode_ == this .isQuery__) {
2758: return true;
2759: } else {
2760: return false;
2761: }
2762: }
2763:
2764: protected void removeClientCursorNameFromCache() {
2765: if (cursorName_ != null
2766: && connection_.clientCursorNameCache_
2767: .containsKey(cursorName_)) {
2768: connection_.clientCursorNameCache_.remove(cursorName_);
2769: }
2770: }
2771:
2772: /**
2773: * Convenience method for resultSetCommitting(ResultSet, boolean)
2774: *
2775: * @see Statement#resultSetCommitting(ResultSet, boolean)
2776: * @param closingRS The ResultSet to be closed
2777: * @throws SqlException
2778: */
2779: public void resultSetCommitting(ResultSet closingRS)
2780: throws SqlException {
2781: resultSetCommitting(closingRS, false);
2782: }
2783:
2784: /**
2785: * Method that checks to see if any other ResultSets are open. If not
2786: * proceeds with the autocommit.
2787: *
2788: * @param closingRS The ResultSet to be closed
2789: * @param writeChain A Boolean indicating whether this method
2790: * is part of a chain of write from client to Server
2791: * @throws SqlException
2792: */
2793: public boolean resultSetCommitting(ResultSet closingRS,
2794: boolean writeChain) throws SqlException {
2795:
2796: // If the Connection is not in auto commit then this statement completion
2797: // cannot cause a commit.
2798: if (!connection_.autoCommit_ || closingRS.autoCommitted_)
2799: return false;
2800:
2801: // If we have multiple results, see if there is another result set open.
2802: // If so, then no commit. The last result set to close will close the statement.
2803: if (resultSetList_ != null) {
2804: for (int i = 0; i < resultSetList_.length; i++) {
2805: ResultSet crs = resultSetList_[i];
2806: if (crs == null)
2807: continue;
2808: if (!crs.openOnClient_)
2809: continue;
2810: if (crs == closingRS)
2811: continue;
2812:
2813: // at least one still open so no commit now.
2814: return false;
2815: }
2816: }
2817:
2818: if (writeChain) {
2819: connection_.writeAutoCommit();
2820: return true;
2821: } else {
2822: if (connection_.flowAutoCommit()) {
2823: markAutoCommitted();
2824: return true;
2825: }
2826: return false;
2827: }
2828: }
2829:
2830: /**
2831: * Mark all ResultSets associated with this statement as auto-committed.
2832: */
2833: public void markAutoCommitted() {
2834: if (resultSetList_ != null) {
2835: for (int i = 0; i < resultSetList_.length; i++)
2836: if (resultSetList_[i] != null) {
2837: resultSetList_[i].markAutoCommitted();
2838: }
2839: } else if (resultSet_ != null) {
2840: resultSet_.markAutoCommitted();
2841: }
2842: }
2843:
2844: protected SQLException jdbc3FeatureNotSupported(
2845: boolean checkStatement) throws SQLException {
2846: try {
2847: if (checkStatement)
2848: checkForClosedStatement();
2849:
2850: throw new SqlException(agent_.logWriter_,
2851: new ClientMessageId(
2852: SQLState.JDBC_METHOD_NOT_IMPLEMENTED));
2853: } catch (SqlException se) {
2854: throw se.getSQLException();
2855: }
2856: }
2857:
2858: protected SQLException jdbc3FeatureNotSupported()
2859: throws SQLException {
2860: return jdbc3FeatureNotSupported(true);
2861: }
2862: }
|