--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.jdbc.EmbedStatement\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.jdbc;\r
+\r
+import org.apache.derby.iapi.reference.JDBC20Translation;\r
+import org.apache.derby.iapi.reference.JDBC30Translation;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.PreparedStatement;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+import org.apache.derby.iapi.sql.ParameterValueSet;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.jdbc.EngineStatement;\r
+\r
+import java.sql.SQLException;\r
+import java.sql.SQLWarning;\r
+import java.util.Vector;\r
+\r
+/*\r
+ We would import these, but have name-overlap\r
+import java.sql.Statement;\r
+import java.sql.ResultSet;\r
+*/\r
+\r
+/**\r
+ *\r
+ * EmbedStatement is a local JDBC statement.\r
+ *\r
+ <P><B>Supports</B>\r
+ <UL>\r
+ <LI> JSR169 - no subsetting for java.sql.Statement\r
+ <LI> JDBC 2.0\r
+ <LI> JDBC 3.0 - no new dependencies on new JDBC 3.0 or JDK 1.4 classes,\r
+ new methods can safely be added into implementation.\r
+ </UL>\r
+\r
+ */\r
+public class EmbedStatement extends ConnectionChild\r
+ implements EngineStatement {\r
+\r
+ private final java.sql.Connection applicationConnection;\r
+ \r
+ /**\r
+ * Statement reference the application is using to execute\r
+ * this Statement. Normally set to this, but if this was\r
+ * created by a Connection from an XAConnection then this\r
+ * will be a reference to the BrokeredStatement.\r
+ *\r
+ * Making it protected to allow access from EmbedPreparedStatement40\r
+ * to be used for StatementEvents\r
+ *\r
+ */\r
+ protected EngineStatement applicationStatement;\r
+\r
+ int updateCount = -1;\r
+ java.sql.ResultSet results;\r
+ //for jdbc3.0 feature, where you can get a resultset of rows inserted\r
+ //for auto generated columns after an insert\r
+ private java.sql.ResultSet autoGeneratedKeysResultSet;\r
+ private String cursorName;\r
+\r
+ private final boolean forMetaData;\r
+ final int resultSetType;\r
+ final int resultSetConcurrency;\r
+ private final int resultSetHoldability;\r
+ final LanguageConnectionContext lcc;\r
+\r
+ private SQLWarning warnings;\r
+ String SQLText;\r
+\r
+ private int fetchSize = 1;\r
+ private int fetchDirection = JDBC20Translation.FETCH_FORWARD;\r
+ int MaxFieldSize;\r
+ /**\r
+ * Query timeout in milliseconds. By default, no statements time\r
+ * out. Timeout is set explicitly with setQueryTimeout().\r
+ */\r
+ long timeoutMillis;\r
+\r
+ //the state of this statement, set to false when close() is called\r
+ private boolean active = true;\r
+\r
+ //in case of batch update, save the individual statements in the batch in this vector\r
+ //this is only used by JDBC 2.0\r
+ Vector batchStatements;\r
+ \r
+ // The maximum # of rows to return per result set.\r
+ // (0 means no limit.)\r
+ int maxRows;\r
+\r
+ private ParameterValueSet pvs;\r
+\r
+ // An EmbedStatement is NOT poolable by default. The constructor for\r
+ // PreparedStatement overrides this.\r
+ protected boolean isPoolable = false;\r
+\r
+ //\r
+ // constructor\r
+ //\r
+ public EmbedStatement (EmbedConnection connection, boolean forMetaData,\r
+ int resultSetType, int resultSetConcurrency, int resultSetHoldability)\r
+ {\r
+ super(connection);\r
+ this.forMetaData = forMetaData;\r
+ this.resultSetType = resultSetType;\r
+ this.resultSetConcurrency = resultSetConcurrency;\r
+ this.resultSetHoldability = resultSetHoldability;\r
+\r
+ lcc = getEmbedConnection().getLanguageConnection();\r
+ applicationConnection = getEmbedConnection().getApplicationConnection();\r
+ applicationStatement = this;\r
+ }\r
+\r
+ //\r
+ // java.sql.Statement interface\r
+ // the comments are those from the JDBC interface,\r
+ // so we know what we're supposed to to.\r
+\r
+ /**\r
+ * Execute a SQL statement that returns a single ResultSet.\r
+ *\r
+ * @param sql typically this is a static SQL SELECT statement\r
+ * @return a ResultSet that contains the data produced by the\r
+ * query; never null\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public java.sql.ResultSet executeQuery(String sql)\r
+ throws SQLException\r
+ {\r
+ execute(sql, true, false, JDBC30Translation.NO_GENERATED_KEYS, null, null);\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (results == null)\r
+ SanityManager.THROWASSERT("no results returned on executeQuery()");\r
+ }\r
+\r
+ return results;\r
+ }\r
+\r
+ /**\r
+ * Execute a SQL INSERT, UPDATE or DELETE statement. In addition,\r
+ * SQL statements that return nothing such as SQL DDL statements\r
+ * can be executed.\r
+ *\r
+ * @param sql a SQL INSERT, UPDATE or DELETE statement or a SQL\r
+ * statement that returns nothing\r
+ * @return either the row count for INSERT, UPDATE or DELETE; or 0\r
+ * for SQL statements that return nothing\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public int executeUpdate(String sql) throws SQLException\r
+ {\r
+ execute(sql, false, true, JDBC30Translation.NO_GENERATED_KEYS, null, null);\r
+ return updateCount;\r
+ }\r
+\r
+ /**\r
+ * JDBC 3.0\r
+ *\r
+ * Execute the given SQL statement and signals the driver with the given flag\r
+ * about whether the auto-generated keys produced by this Statement object\r
+ * should be made available for retrieval.\r
+ *\r
+ * @param sql a SQL INSERT, UPDATE or DELETE statement or a SQL\r
+ * statement that returns nothing\r
+ * @param autoGeneratedKeys - a flag indicating whether auto-generated keys\r
+ * should be made available for retrieval; one of the following constants:\r
+ * Statement.RETURN_GENERATED_KEYS Statement.NO_GENERATED_KEYS\r
+ * @return either the row count for INSERT, UPDATE or DELETE; or 0\r
+ * for SQL statements that return nothing\r
+ * @exception SQLException if a database access error occurs\r
+ */\r
+ public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException\r
+ {\r
+ execute(sql, false, true, autoGeneratedKeys, null, null);\r
+ return updateCount;\r
+ }\r
+\r
+ /**\r
+ * JDBC 3.0\r
+ *\r
+ * Executes the given SQL statement and signals the driver that the\r
+ * auto-generated keys indicated in the given array should be made\r
+ * available for retrieval. The driver will ignore the array if the SQL\r
+ * statement is not an INSERT statement\r
+ *\r
+ * @param sql a SQL INSERT, UPDATE or DELETE statement or a SQL\r
+ * statement that returns nothing\r
+ * @param columnIndexes - an array of column indexes indicating the\r
+ * columns that should be returned from the inserted row\r
+ * @return either the row count for INSERT, UPDATE or DELETE; or 0\r
+ * for SQL statements that return nothing\r
+ * @exception SQLException if a database access error occurs\r
+ */\r
+ public int executeUpdate(String sql, int[] columnIndexes) throws SQLException\r
+ {\r
+ execute(sql, false, true,\r
+ ((columnIndexes == null) || (columnIndexes.length == 0))\r
+ ? JDBC30Translation.NO_GENERATED_KEYS\r
+ : JDBC30Translation.RETURN_GENERATED_KEYS,\r
+ columnIndexes,\r
+ null);\r
+ return updateCount;\r
+ }\r
+\r
+ /**\r
+ * JDBC 3.0\r
+ *\r
+ * Executes the given SQL statement and signals the driver that the\r
+ * auto-generated keys indicated in the given array should be made\r
+ * available for retrieval. The driver will ignore the array if the SQL\r
+ * statement is not an INSERT statement\r
+ *\r
+ * @param sql a SQL INSERT, UPDATE or DELETE statement or a SQL\r
+ * statement that returns nothing\r
+ * @param columnNames - an array of the names of the columns\r
+ * that should be returned from the inserted row\r
+ * @return either the row count for INSERT, UPDATE or DELETE; or 0\r
+ * for SQL statements that return nothing\r
+ * @exception SQLException if a database access error occurs\r
+ */\r
+ public int executeUpdate(String sql, String[] columnNames) throws SQLException\r
+ {\r
+ execute(sql, false, true,\r
+ ((columnNames == null) || (columnNames.length == 0))\r
+ ? JDBC30Translation.NO_GENERATED_KEYS\r
+ : JDBC30Translation.RETURN_GENERATED_KEYS,\r
+ null,\r
+ columnNames);\r
+ return updateCount;\r
+ }\r
+\r
+ final void checkIfInMiddleOfBatch() throws SQLException {\r
+ /* If batchStatements is not null then we are in the middle\r
+ * of a batch. That's an invalid state. We need to finish the\r
+ * batch either by clearing the batch or executing the batch.\r
+ * executeUpdate is not allowed inside the batch.\r
+ */\r
+ if (batchStatements != null)\r
+ throw newSQLException(SQLState.MIDDLE_OF_BATCH);\r
+ }\r
+\r
+ /**\r
+ * Tell whether this statment has been closed or not.\r
+ *\r
+ * @return <code>true</code> is closed, <code>false</code> otherwise.\r
+ * @exception SQLException if a database access error occurs.\r
+ */\r
+ public boolean isClosed() throws SQLException {\r
+ // If active, verify state by consulting parent connection.\r
+ if (active) {\r
+ try {\r
+ checkExecStatus();\r
+ } catch (SQLException sqle) {\r
+ }\r
+ }\r
+ return !active;\r
+ }\r
+\r
+ /**\r
+ * In many cases, it is desirable to immediately release a\r
+ * Statements's database and JDBC resources instead of waiting for\r
+ * this to happen when it is automatically closed; the close\r
+ * method provides this immediate release.\r
+ *\r
+ * <P><B>Note:</B> A Statement is automatically closed when it is\r
+ * garbage collected. When a Statement is closed its current\r
+ * ResultSet, if one exists, is also closed.\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public final void close() throws SQLException {\r
+\r
+ /* The close() method is the only method\r
+ * that is allowed to be called on a closed\r
+ * Statement, as per Jon Ellis.\r
+ */\r
+ if (!active)\r
+ {\r
+ return;\r
+ }\r
+\r
+ synchronized (getConnectionSynchronization()) {\r
+\r
+ closeActions();\r
+ \r
+ //we first set the status\r
+ active = false;\r
+\r
+ //first, clear the resutl set\r
+ clearResultSets();\r
+ \r
+ //next, release other resource\r
+ cursorName = null;\r
+ warnings = null;\r
+ SQLText = null;\r
+ batchStatements = null;\r
+ }\r
+ }\r
+\r
+ // allow sub-classes to execute additional close\r
+ // logic while holding the synchronization.\r
+ void closeActions() throws SQLException {\r
+ }\r
+\r
+ //----------------------------------------------------------------------\r
+\r
+ /**\r
+ * The maxFieldSize limit (in bytes) is the maximum amount of data\r
+ * returned for any column value; it only applies to BINARY,\r
+ * VARBINARY, LONGVARBINARY, CHAR, VARCHAR, and LONGVARCHAR\r
+ * columns. If the limit is exceeded, the excess data is silently\r
+ * discarded.\r
+ *\r
+ * @return the current max column size limit; zero means unlimited\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public int getMaxFieldSize() throws SQLException {\r
+ checkStatus();\r
+\r
+ return MaxFieldSize;\r
+ }\r
+\r
+ /**\r
+ * The maxFieldSize limit (in bytes) is set to limit the size of\r
+ * data that can be returned for any column value; it only applies\r
+ * to BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR, and\r
+ * LONGVARCHAR fields. If the limit is exceeded, the excess data\r
+ * is silently discarded.\r
+ *\r
+ * @param max the new max column size limit; zero means unlimited\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public void setMaxFieldSize(int max) throws SQLException {\r
+ checkStatus();\r
+\r
+ if (max < 0)\r
+ {\r
+ throw newSQLException(SQLState.INVALID_MAXFIELD_SIZE, new Integer(max));\r
+ }\r
+ this.MaxFieldSize = max;\r
+ }\r
+\r
+ /**\r
+ * The maxRows limit is the maximum number of rows that a\r
+ * ResultSet can contain. If the limit is exceeded, the excess\r
+ * rows are silently dropped.\r
+ *\r
+ * @return the current max row limit; zero means unlimited\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public int getMaxRows() throws SQLException \r
+ {\r
+ checkStatus();\r
+ return maxRows;\r
+ }\r
+\r
+ /**\r
+ * The maxRows limit is set to limit the number of rows that any\r
+ * ResultSet can contain. If the limit is exceeded, the excess\r
+ * rows are silently dropped.\r
+ *\r
+ * @param max the new max rows limit; zero means unlimited\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public void setMaxRows(int max) throws SQLException \r
+ {\r
+ checkStatus();\r
+ if (max < 0)\r
+ {\r
+ throw newSQLException(SQLState.INVALID_MAX_ROWS_VALUE, new Integer(max));\r
+ }\r
+ this.maxRows = max;\r
+ }\r
+\r
+ /**\r
+ * If escape scanning is on (the default) the driver will do\r
+ * escape substitution before sending the SQL to the database.\r
+ *\r
+ * @param enable true to enable; false to disable\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public void setEscapeProcessing(boolean enable) throws SQLException {\r
+ checkStatus();\r
+ // Nothing to do in our server , just ignore it.\r
+\r
+ }\r
+\r
+ /**\r
+ * The queryTimeout limit is the number of seconds the driver will\r
+ * wait for a Statement to execute. If the limit is exceeded a\r
+ * SQLException is thrown.\r
+ *\r
+ * @return the current query timeout limit in seconds; zero means unlimited\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public final int getQueryTimeout() throws SQLException {\r
+ checkStatus();\r
+ return (int) (timeoutMillis / 1000);\r
+ }\r
+\r
+ /**\r
+ * The queryTimeout limit is the number of seconds the driver will\r
+ * wait for a Statement to execute. If the limit is exceeded a\r
+ * SQLException is thrown.\r
+ *\r
+ * @param seconds the new query timeout limit in seconds; zero means unlimited\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public final void setQueryTimeout(int seconds) throws SQLException {\r
+ checkStatus();\r
+ if (seconds < 0) {\r
+ throw newSQLException(SQLState.INVALID_QUERYTIMEOUT_VALUE,\r
+ new Integer(seconds));\r
+ }\r
+ timeoutMillis = (long) seconds * 1000;\r
+ }\r
+\r
+ /**\r
+ * Cancel can be used by one thread to cancel a statement that\r
+ * is being executed by another thread.\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public void cancel() throws SQLException {\r
+ throw Util.notImplemented("cancel");\r
+ }\r
+\r
+ /**\r
+ * The first warning reported by calls on this Statement is\r
+ * returned. A Statment's execute methods clear its SQLWarning\r
+ * chain. Subsequent Statement warnings will be chained to this\r
+ * SQLWarning.\r
+ *\r
+ * <p>The warning chain is automatically cleared each time\r
+ * a statement is (re)executed.\r
+ *\r
+ * <P><B>Note:</B> If you are processing a ResultSet then any\r
+ * warnings associated with ResultSet reads will be chained on the\r
+ * ResultSet object.\r
+ *\r
+ * @return the first SQLWarning or null\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public SQLWarning getWarnings() throws SQLException {\r
+ checkStatus();\r
+ return warnings;\r
+ }\r
+\r
+ /**\r
+ * After this call getWarnings returns null until a new warning is\r
+ * reported for this Statement.\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public void clearWarnings() throws SQLException {\r
+ checkStatus();\r
+ warnings = null;\r
+ }\r
+\r
+ /**\r
+ * setCursorName defines the SQL cursor name that will be used by\r
+ * subsequent Statement execute methods. This name can then be\r
+ * used in SQL positioned update/delete statements to identify the\r
+ * current row in the ResultSet generated by this statement. If\r
+ * the database doesn't support positioned update/delete, this\r
+ * method is a noop.\r
+ *\r
+ * <P><B>Note:</B> By definition, positioned update/delete\r
+ * execution must be done by a different Statement than the one\r
+ * which generated the ResultSet being used for positioning. Also,\r
+ * cursor names must be unique within a Connection.\r
+ *\r
+ * @param name the new cursor name.\r
+ */\r
+ public void setCursorName(String name) throws SQLException {\r
+ checkStatus();\r
+ cursorName = name;\r
+ }\r
+\r
+ //----------------------- Multiple Results --------------------------\r
+\r
+ /**\r
+ * Execute a SQL statement that may return multiple results.\r
+ * Under some (uncommon) situations a single SQL statement may return\r
+ * multiple result sets and/or update counts. Normally you can ignore\r
+ * this, unless you're executing a stored procedure that you know may\r
+ * return multiple results, or unless you're dynamically executing an\r
+ * unknown SQL string. The "execute", "getMoreResults", "getResultSet"\r
+ * and "getUpdateCount" methods let you navigate through multiple results.\r
+ *\r
+ * The "execute" method executes a SQL statement and indicates the\r
+ * form of the first result. You can then use getResultSet or\r
+ * getUpdateCount to retrieve the result, and getMoreResults to\r
+ * move to any subsequent result(s).\r
+ *\r
+ * @param sql any SQL statement\r
+ *\r
+ * @return true if the first result is a ResultSet; false if it is an integer\r
+ * @see #getResultSet\r
+ * @see #getUpdateCount\r
+ * @see #getMoreResults\r
+ * @exception SQLException thrown on failure\r
+ */\r
+ public boolean execute(String sql)\r
+ throws SQLException\r
+ {\r
+ return execute(sql, false, false, JDBC30Translation.NO_GENERATED_KEYS, null, null);\r
+ }\r
+ \r
+ /**\r
+ * Execute a SQL statement that may return multiple results.\r
+ * Under some (uncommon) situations a single SQL statement may return\r
+ * multiple result sets and/or update counts. Normally you can ignore\r
+ * this, unless you're executing a stored procedure that you know may\r
+ * return multiple results, or unless you're dynamically executing an\r
+ * unknown SQL string. The "execute", "getMoreResults", "getResultSet"\r
+ * and "getUpdateCount" methods let you navigate through multiple results.\r
+ *\r
+ * The "execute" method executes a SQL statement and indicates the\r
+ * form of the first result. You can then use getResultSet or\r
+ * getUpdateCount to retrieve the result, and getMoreResults to\r
+ * move to any subsequent result(s).\r
+ *\r
+ * @param sql any SQL statement\r
+ * @param executeQuery caller is executeQuery()\r
+ * @param executeUpdate caller is executeUpdate()\r
+ * @param autoGeneratedKeys\r
+ * @param columnIndexes\r
+ * @param columnNames\r
+ *\r
+ * @return true if the first result is a ResultSet; false if it is an integer\r
+ * @see #getResultSet\r
+ * @see #getUpdateCount\r
+ * @see #getMoreResults\r
+ * @exception SQLException thrown on failure\r
+ */\r
+ private boolean execute(String sql, boolean executeQuery, boolean executeUpdate,\r
+ int autoGeneratedKeys, int[] columnIndexes, String[] columnNames) throws SQLException\r
+ {\r
+ synchronized (getConnectionSynchronization()) {\r
+\r
+ checkExecStatus();\r
+ if (sql == null) {\r
+ throw newSQLException(SQLState.NULL_SQL_TEXT);\r
+ }\r
+ checkIfInMiddleOfBatch();\r
+ clearResultSets(); // release the last statement executed, if any.\r
+\r
+ setupContextStack(); // make sure there's context\r
+\r
+\r
+ // try to remember the SQL statement in case anybody asks for it\r
+ SQLText = sql; \r
+\r
+ try {\r
+ Activation activation;\r
+ try {\r
+ PreparedStatement preparedStatement = lcc.prepareInternalStatement\r
+ (lcc.getDefaultSchema(), sql, resultSetConcurrency==JDBC20Translation.CONCUR_READ_ONLY, false);\r
+ activation =\r
+ preparedStatement.getActivation(lcc, resultSetType == JDBC20Translation.TYPE_SCROLL_INSENSITIVE);\r
+ checkRequiresCallableStatement(activation);\r
+ } catch (Throwable t) {\r
+ throw handleException(t);\r
+ }\r
+\r
+\r
+ // this is for a Statement execution\r
+ activation.setSingleExecution();\r
+\r
+ //bug 4838 - save the auto-generated key information in activation. keeping this\r
+ //information in lcc will not work work it can be tampered by a nested trasaction\r
+ if (autoGeneratedKeys == JDBC30Translation.RETURN_GENERATED_KEYS)\r
+ activation.setAutoGeneratedKeysResultsetInfo(columnIndexes, columnNames);\r
+ return executeStatement(activation, executeQuery, executeUpdate);\r
+ } finally {\r
+ restoreContextStack();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * JDBC 3.0\r
+ *\r
+ * Executes the given SQL statement, which may return multiple\r
+ * results, and signals the driver that any auto-generated keys\r
+ * should be made available for retrieval. The driver will ignore\r
+ * this signal if the SQL statement is not an INSERT statement.\r
+ *\r
+ * @param sql any SQL statement\r
+ * @param autoGeneratedKeys - a constant indicating whether\r
+ * auto-generated keys should be made available for retrieval using\r
+ * the method getGeneratedKeys; one of the following constants:\r
+ * Statement.RETURN_GENERATED_KEYS or Statement.NO_GENERATED_KEYS\r
+ * @return rue if the first result is a ResultSet object; false if\r
+ * it is an update count or there are no results\r
+ * @exception SQLException if a database access error occurs\r
+ */\r
+ public boolean execute(String sql, int autoGeneratedKeys) throws SQLException\r
+ {\r
+ return execute(sql, false, false, autoGeneratedKeys, null, null);\r
+ }\r
+\r
+ /**\r
+ * JDBC 3.0\r
+ *\r
+ * Executes the given SQL statement, which may return multiple\r
+ * results, and signals the driver that the auto-generated keys\r
+ * indicated in the given array should be made available for retrieval.\r
+ * This array contains the indexes of the columns in the target table\r
+ * that contain the auto-generated keys that should be made available.\r
+ * The driver will ignore the array if the given SQL statement is not an\r
+ * INSERT statement.\r
+ *\r
+ * @param sql any SQL statement\r
+ * @param columnIndexes - an array of the indexes of the columns in the\r
+ * inserted row that should be made available for retrieval by a call to\r
+ * the method getGeneratedKeys\r
+ * @return rue if the first result is a ResultSet object; false if\r
+ * it is an update count or there are no results\r
+ * @exception SQLException if a database access error occurs\r
+ */\r
+ public boolean execute(String sql, int[] columnIndexes) throws SQLException\r
+ {\r
+ return execute(sql, false, true,\r
+ ((columnIndexes == null) || (columnIndexes.length == 0))\r
+ ? JDBC30Translation.NO_GENERATED_KEYS\r
+ : JDBC30Translation.RETURN_GENERATED_KEYS,\r
+ columnIndexes,\r
+ null);\r
+ }\r
+\r
+ /**\r
+ * JDBC 3.0\r
+ *\r
+ * Executes the given SQL statement, which may return multiple\r
+ * results, and signals the driver that the auto-generated keys\r
+ * indicated in the given array should be made available for retrieval.\r
+ * This array contains the names of the columns in the target table\r
+ * that contain the auto-generated keys that should be made available.\r
+ * The driver will ignore the array if the given SQL statement is not an\r
+ * INSERT statement.\r
+ *\r
+ * @param sql any SQL statement\r
+ * @param columnNames - an array of the names of the columns in the\r
+ * inserted row that should be made available for retrieval by a call to\r
+ * the method getGeneratedKeys\r
+ * @return rue if the first result is a ResultSet object; false if\r
+ * it is an update count or there are no results\r
+ * @exception SQLException if a database access error occurs\r
+ */\r
+ public boolean execute(String sql, String[] columnNames) throws SQLException\r
+ {\r
+ return execute(sql, false, true,\r
+ ((columnNames == null) || (columnNames.length == 0))\r
+ ? JDBC30Translation.NO_GENERATED_KEYS\r
+ : JDBC30Translation.RETURN_GENERATED_KEYS,\r
+ null,\r
+ columnNames);\r
+ }\r
+\r
+ /**\r
+ * getResultSet returns the current result as a ResultSet. It\r
+ * should only be called once per result.\r
+ *\r
+ * @return the current result as a ResultSet; null if the result\r
+ * is an update count or there are no more results or the statement\r
+ * was closed.\r
+ * @see #execute\r
+ */\r
+ public final java.sql.ResultSet getResultSet() throws SQLException {\r
+ checkStatus();\r
+\r
+ return results;\r
+ }\r
+\r
+ /**\r
+ * getUpdateCount returns the current result as an update count;\r
+ * if the result is a ResultSet or there are no more results -1\r
+ * is returned. It should only be called once per result.\r
+ *\r
+ * <P>The only way to tell for sure that the result is an update\r
+ * count is to first test to see if it is a ResultSet. If it is\r
+ * not a ResultSet it is either an update count or there are no\r
+ * more results.\r
+ *\r
+ * @return the current result as an update count; -1 if it is a\r
+ * ResultSet or there are no more results\r
+ * @see #execute\r
+ */\r
+ public final int getUpdateCount() throws SQLException {\r
+ checkStatus();\r
+ return updateCount;\r
+ }\r
+\r
+ /**\r
+ * getMoreResults moves to a Statement's next result. It returns true if\r
+ * this result is a ResultSet. getMoreResults also implicitly\r
+ * closes any current ResultSet obtained with getResultSet.\r
+ *\r
+ * There are no more results when (!getMoreResults() &&\r
+ * (getUpdateCount() == -1)\r
+ *\r
+ * @return true if the next result is a ResultSet; false if it is\r
+ * an update count or there are no more results\r
+ * @see #execute\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public final boolean getMoreResults() throws SQLException {\r
+ return getMoreResults(JDBC30Translation.CLOSE_ALL_RESULTS);\r
+ }\r
+\r
+ /////////////////////////////////////////////////////////////////////////\r
+ //\r
+ // JDBC 2.0 methods that are implemented here because EmbedPreparedStatement\r
+ // and EmbedCallableStatement in Local20 need access to them, and those\r
+ // classes extend their peer classes in Local, instead of EmbedStatement\r
+ // in Local20\r
+ //\r
+ // We do the same of JDBC 3.0 methods.\r
+ /////////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ * JDBC 2.0\r
+ *\r
+ * Determine the result set type.\r
+ *\r
+ * @exception SQLException Feature not implemented for now.\r
+ */\r
+ public final int getResultSetType()\r
+ throws SQLException \r
+ {\r
+ checkStatus();\r
+ return resultSetType;\r
+ }\r
+\r
+\r
+ /**\r
+ * JDBC 2.0\r
+ *\r
+ * Give a hint as to the direction in which the rows in a result set\r
+ * will be processed. The hint applies only to result sets created\r
+ * using this Statement object. The default value is \r
+ * ResultSet.FETCH_FORWARD.\r
+ *\r
+ * @param direction the initial direction for processing rows\r
+ * @exception SQLException if a database-access error occurs or direction\r
+ * is not one of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or\r
+ * ResultSet.FETCH_UNKNOWN\r
+ */\r
+ public void setFetchDirection(int direction) throws SQLException {\r
+ \r
+ checkStatus();\r
+ /* fetch direction is meaningless to us. we just save\r
+ * it off if it is valid and return the current value if asked.\r
+ */\r
+ if (direction == JDBC20Translation.FETCH_FORWARD || \r
+ direction == JDBC20Translation.FETCH_REVERSE ||\r
+ direction == JDBC20Translation.FETCH_UNKNOWN )\r
+ {\r
+ fetchDirection = direction;\r
+ }else\r
+ throw newSQLException(SQLState.INVALID_FETCH_DIRECTION, \r
+ new Integer(direction));\r
+ }\r
+\r
+ /**\r
+ * JDBC 2.0\r
+ *\r
+ * Determine the fetch direction.\r
+ *\r
+ * @return the default fetch direction\r
+ * @exception SQLException if a database-access error occurs\r
+ */\r
+ public int getFetchDirection() throws SQLException {\r
+ checkStatus();\r
+ return fetchDirection;\r
+ }\r
+\r
+\r
+ /**\r
+ * JDBC 2.0\r
+ *\r
+ * Give the JDBC driver a hint as to the number of rows that should\r
+ * be fetched from the database when more rows are needed. The number \r
+ * of rows specified only affects result sets created using this \r
+ * statement. If the value specified is zero, then the hint is ignored.\r
+ * The default value is zero.\r
+ *\r
+ * @param rows the number of rows to fetch\r
+ * @exception SQLException if a database-access error occurs, or the\r
+ * condition 0 <= rows <= this.getMaxRows() is not satisfied.\r
+ */\r
+ public void setFetchSize(int rows) throws SQLException {\r
+ checkStatus();\r
+ if (rows < 0 || (this.getMaxRows() != 0 && \r
+ rows > this.getMaxRows()))\r
+ {\r
+ throw newSQLException(SQLState.INVALID_ST_FETCH_SIZE, new Integer(rows));\r
+ }else if ( rows > 0 ) // ignore the call if the value is zero\r
+ fetchSize = rows;\r
+ }\r
+ \r
+ /**\r
+ * JDBC 2.0\r
+ *\r
+ * Determine the default fetch size.\r
+ * @exception SQLException if a database-access error occurs\r
+ *\r
+ */\r
+ public int getFetchSize() throws SQLException {\r
+ checkStatus();\r
+ return fetchSize;\r
+ }\r
+\r
+ /**\r
+ * JDBC 2.0\r
+ *\r
+ * Determine the result set concurrency.\r
+ *\r
+ * @exception SQLException Feature not implemented for now.\r
+ */\r
+ public int getResultSetConcurrency() throws SQLException {\r
+ checkStatus();\r
+ return resultSetConcurrency;\r
+ }\r
+\r
+ /**\r
+ * JDBC 3.0\r
+ *\r
+ * Retrieves the result set holdability for ResultSet objects\r
+ * generated by this Statement object.\r
+ *\r
+ * @return either ResultSet.HOLD_CURSORS_OVER_COMMIT or\r
+ * ResultSet.CLOSE_CURSORS_AT_COMMIT\r
+ * @exception SQLException Feature not implemented for now.\r
+ */\r
+ public final int getResultSetHoldability() throws SQLException {\r
+ checkStatus();\r
+ return resultSetHoldability;\r
+ }\r
+\r
+ /**\r
+ * JDBC 2.0\r
+ *\r
+ * Adds a SQL command to the current batch of commmands for the statement.\r
+ * This method is optional.\r
+ *\r
+ * @param sql typically this is a static SQL INSERT or UPDATE statement\r
+ * @exception SQLException if a database-access error occurs, or the\r
+ * driver does not support batch statements\r
+ */\r
+ public void addBatch( String sql ) throws SQLException {\r
+ checkStatus();\r
+ synchronized (getConnectionSynchronization()) {\r
+ if (batchStatements == null)\r
+ batchStatements = new Vector();\r
+ batchStatements.addElement(sql);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * JDBC 2.0\r
+ *\r
+ * Make the set of commands in the current batch empty.\r
+ * This method is optional.\r
+ *\r
+ * @exception SQLException if a database-access error occurs, or the\r
+ * driver does not support batch statements\r
+ */\r
+ public final void clearBatch() throws SQLException {\r
+ checkStatus();\r
+ synchronized (getConnectionSynchronization()) {\r
+ batchStatements = null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * JDBC 2.0\r
+ * \r
+ * Submit a batch of commands to the database for execution.\r
+ * This method is optional.\r
+ *\r
+ * Moving jdbc2.0 batch related code in this class because\r
+ * callableStatement in jdbc 20 needs this code too and it doesn't derive\r
+ * from prepared statement in jdbc 20 in our implementation. \r
+ * BatchUpdateException is the only new class from jdbc 20 which is being\r
+ * referenced here and in order to avoid any jdk11x problems, using\r
+ * reflection code to make an instance of that class. \r
+ *\r
+ * @return an array of update counts containing one element for each\r
+ * command in the batch. The array is ordered according\r
+ * to the order in which commands were inserted into the batch\r
+ * @exception SQLException if a database-access error occurs, or the\r
+ * driver does not support batch statements\r
+ */\r
+ public int[] executeBatch() throws SQLException {\r
+ checkExecStatus();\r
+ synchronized (getConnectionSynchronization()) \r
+ {\r
+ setupContextStack();\r
+ int i = 0;\r
+ // As per the jdbc 2.0 specs, close the statement object's current resultset\r
+ // if one is open.\r
+ // Are there results?\r
+ // outside of the lower try/finally since results will\r
+ // setup and restore themselves.\r
+ clearResultSets();\r
+\r
+ Vector stmts = batchStatements;\r
+ batchStatements = null;\r
+ int size;\r
+ if (stmts == null)\r
+ size = 0;\r
+ else\r
+ size = stmts.size();\r
+\r
+ int[] returnUpdateCountForBatch = new int[size];\r
+\r
+ SQLException sqle;\r
+ try {\r
+ for (; i< size; i++) \r
+ {\r
+ if (executeBatchElement(stmts.elementAt(i)))\r
+ throw newSQLException(SQLState.RESULTSET_RETURN_NOT_ALLOWED);\r
+ returnUpdateCountForBatch[i] = getUpdateCount();\r
+ }\r
+ return returnUpdateCountForBatch;\r
+ }\r
+ catch (StandardException se) {\r
+\r
+ sqle = handleException(se);\r
+ }\r
+ catch (SQLException sqle2) \r
+ {\r
+ sqle = sqle2;\r
+ }\r
+ finally \r
+ {\r
+ restoreContextStack();\r
+ }\r
+\r
+ int successfulUpdateCount[] = new int[i];\r
+ for (int j=0; j<i; j++)\r
+ {\r
+ successfulUpdateCount[j] = returnUpdateCountForBatch[j];\r
+ }\r
+\r
+ SQLException batch =\r
+ new java.sql.BatchUpdateException(sqle.getMessage(), sqle.getSQLState(),\r
+ sqle.getErrorCode(), successfulUpdateCount);\r
+\r
+ batch.setNextException(sqle);\r
+ batch.initCause(sqle);\r
+ throw batch;\r
+ }\r
+ }\r
+\r
+ /**\r
+ Execute a single element of the batch. Overridden by EmbedPreparedStatement\r
+ */\r
+ boolean executeBatchElement(Object batchElement) throws SQLException, StandardException {\r
+ return execute((String)batchElement, false, true, JDBC30Translation.NO_GENERATED_KEYS, null, null);\r
+ }\r
+\r
+ /**\r
+ * JDBC 2.0\r
+ *\r
+ * Return the Connection that produced the Statement.\r
+ *\r
+ * @exception SQLException Exception if it cannot find the connection\r
+ * associated to this statement.\r
+ */\r
+ public final java.sql.Connection getConnection() throws SQLException {\r
+ checkStatus();\r
+\r
+ java.sql.Connection appConn = getEmbedConnection().getApplicationConnection();\r
+ if ((appConn != applicationConnection) || (appConn == null)) {\r
+\r
+ throw Util.noCurrentConnection();\r
+ }\r
+ return appConn;\r
+ }\r
+\r
+ /**\r
+ * JDBC 3.0\r
+ *\r
+ * Moves to this Statement obect's next result, deals with any current ResultSet\r
+ * object(s) according to the instructions specified by the given flag, and\r
+ * returns true if the next result is a ResultSet object\r
+ *\r
+ * @param current - one of the following Statement constants indicating what\r
+ * should happen to current ResultSet objects obtained using the method\r
+ * getResultSetCLOSE_CURRENT_RESULT, KEEP_CURRENT_RESULT, or CLOSE_ALL_RESULTS\r
+ * @return true if the next result is a ResultSet; false if it is\r
+ * an update count or there are no more results\r
+ * @see #execute\r
+ * @exception SQLException thrown on failure.\r
+ */\r
+ public final boolean getMoreResults(int current) throws SQLException {\r
+ checkExecStatus();\r
+\r
+ synchronized (getConnectionSynchronization()) {\r
+ if (dynamicResults == null) {\r
+ // we only have the one resultset, so this is\r
+ // simply a close for us.\r
+ clearResultSets();\r
+ return false;\r
+ }\r
+\r
+ int startingClose;\r
+ switch (current) {\r
+ default:\r
+ case JDBC30Translation.CLOSE_ALL_RESULTS:\r
+ startingClose = 0;\r
+ break;\r
+ case JDBC30Translation.CLOSE_CURRENT_RESULT:\r
+ // just close the current result set.\r
+ startingClose = currentDynamicResultSet;\r
+ break;\r
+ case JDBC30Translation.KEEP_CURRENT_RESULT:\r
+ // make the close loop a no-op.\r
+ startingClose = dynamicResults.length;\r
+ break;\r
+ }\r
+\r
+ // Close loop.\r
+ SQLException se = null;\r
+ for (int i = startingClose; i <= currentDynamicResultSet && i < dynamicResults.length; i++) {\r
+ EmbedResultSet lrs = dynamicResults[i];\r
+ if (lrs == null)\r
+ continue;\r
+\r
+\r
+ try {\r
+ lrs.close();\r
+ } catch (SQLException sqle) {\r
+ if (se == null)\r
+ se = sqle;\r
+ else\r
+ se.setNextException(sqle);\r
+ } finally {\r
+ dynamicResults[i] = null;\r
+ }\r
+ }\r
+\r
+ if (se != null) {\r
+ // leave positioned on the current result set (?)\r
+ throw se;\r
+ }\r
+\r
+ updateCount = -1;\r
+\r
+ while (++currentDynamicResultSet < dynamicResults.length) {\r
+\r
+ EmbedResultSet lrs = dynamicResults[currentDynamicResultSet];\r
+ if (lrs != null) {\r
+ if (lrs.isClosed) {\r
+ dynamicResults[currentDynamicResultSet] = null;\r
+ continue;\r
+ }\r
+\r
+ results = lrs;\r
+\r
+ return true;\r
+ }\r
+ }\r
+\r
+ results = null;\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * JDBC 3.0\r
+ *\r
+ * Retrieves any auto-generated keys created as a result of executing this\r
+ * Statement object. If this Statement is a non-insert statement,\r
+ * a null ResultSet object is returned.\r
+ *\r
+ * @return a ResultSet object containing the auto-generated key(s) generated by\r
+ * the execution of this Statement object\r
+ * @exception SQLException if a database access error occurs\r
+ */\r
+ public final java.sql.ResultSet getGeneratedKeys() throws SQLException {\r
+ checkStatus();\r
+ if (autoGeneratedKeysResultSet == null)\r
+ return null;\r
+ else {\r
+ execute("VALUES IDENTITY_VAL_LOCAL()", true, false, JDBC30Translation.NO_GENERATED_KEYS, null, null);\r
+ return results;\r
+ }\r
+ }\r
+\r
+ /////////////////////////////////////////////////////////////////////////\r
+ //\r
+ // Implementation specific methods \r
+ //\r
+ /////////////////////////////////////////////////////////////////////////\r
+\r
+ /**\r
+ Execute the current statement.\r
+ @exception SQLException thrown on failure.\r
+ */\r
+ boolean executeStatement(Activation a,\r
+ boolean executeQuery, boolean executeUpdate)\r
+ throws SQLException {\r
+\r
+ // we don't differentiate the update from the resultset case.\r
+ // so, there could be a result set.\r
+\r
+ // note: the statement interface will paste together\r
+ // an activation and make sure the prepared statement\r
+ // is still valid, so it is preferrable, for now,\r
+ // to creating our own activation and stuffing it in\r
+ // the prepared statement.\r
+\r
+ synchronized (getConnectionSynchronization()) {\r
+ setupContextStack(); // make sure there's context\r
+ boolean retval;\r
+\r
+ pvs = a.getParameterValueSet();\r
+\r
+ try {\r
+ // The following is from the javadoc for java.sql.Statement\r
+ // Only one ResultSet per Statement can be open at any point in time.\r
+ // Therefore, if the reading of one ResultSet is interleaved with the\r
+ // reading of another, each must have been generated by different Statements.\r
+ // All statement execute methods implicitly close a\r
+ // statment's current ResultSet if an open one exists. \r
+ if (results != null) {\r
+ results.close();\r
+ results = null;\r
+ }\r
+\r
+ clearWarnings();\r
+\r
+ if (! forMetaData) {\r
+ commitIfNeeded(); // commit the last statement if needed\r
+ needCommit();\r
+ } else {\r
+\r
+\r
+ if (lcc.getActivationCount() > 1) {\r
+ // we do not want to commit here as there seems to be other\r
+ // statements/resultSets currently opened for this connection.\r
+ } else {\r
+ commitIfNeeded(); // we can legitimately commit\r
+ needCommit();\r
+ }\r
+ }\r
+\r
+ // if this was a prepared statement, this just\r
+ // gets it for us, it won't recompile unless it is invalid.\r
+ PreparedStatement ps = a.getPreparedStatement();\r
+ ps.rePrepare(lcc);\r
+ addWarning(ps.getCompileTimeWarnings());\r
+\r
+\r
+ /*\r
+ ** WARNING WARNING\r
+ **\r
+ ** Any state set in the activation before execution *must* be copied\r
+ ** to the new activation in GenericActivationHolder.execute() when\r
+ ** the statement has been recompiled. State such as\r
+ ** singleExecution, cursorName, holdability, maxRows.\r
+ */\r
+\r
+ if (cursorName != null)\r
+ {\r
+ a.setCursorName(cursorName);\r
+ }\r
+ \r
+ boolean executeHoldable = getExecuteHoldable();\r
+ \r
+ a.setResultSetHoldability(executeHoldable);\r
+\r
+ //reset the activation to clear warnings\r
+ //and clear existing result sets in case this has been cached\r
+ a.reset();\r
+ a.setMaxRows(maxRows);\r
+ ResultSet resultsToWrap = ps.execute(a,\r
+ false,\r
+ timeoutMillis);\r
+ addWarning(a.getWarnings());\r
+\r
+\r
+ if (resultsToWrap.returnsRows()) {\r
+\r
+ // The statement returns rows, so calling it with\r
+ // executeUpdate() is not allowed.\r
+ if (executeUpdate) {\r
+ throw StandardException.newException(\r
+ SQLState.LANG_INVALID_CALL_TO_EXECUTE_UPDATE);\r
+ }\r
+\r
+ EmbedResultSet lresults = factory.newEmbedResultSet(getEmbedConnection(), resultsToWrap, forMetaData, this, ps.isAtomic());\r
+ results = lresults;\r
+\r
+\r
+ // Set up the finalization of the ResultSet to\r
+ // mark the activation as unused. It will be\r
+ // closed sometime later by the connection\r
+ // outside of finalization.\r
+ if (a.isSingleExecution())\r
+ lresults.singleUseActivation = a;\r
+\r
+ updateCount = -1;\r
+ retval = true;\r
+ }\r
+ else {\r
+\r
+ // Only applipable for an insert statement, which does not return rows.\r
+ //the auto-generated keys resultset will be null if used for non-insert statement\r
+ if (a.getAutoGeneratedKeysResultsetMode() && (resultsToWrap.getAutoGeneratedKeysResultset() != null))\r
+ {\r
+ resultsToWrap.getAutoGeneratedKeysResultset().open();\r
+ autoGeneratedKeysResultSet = factory.newEmbedResultSet(getEmbedConnection(),\r
+ resultsToWrap.getAutoGeneratedKeysResultset(), false, this, ps.isAtomic());\r
+ }\r
+\r
+ updateCount = resultsToWrap.modifiedRowCount();\r
+\r
+ results = null; // note that we have none.\r
+\r
+ int dynamicResultCount = 0;\r
+ if (a.getDynamicResults() != null) {\r
+ dynamicResultCount =\r
+ processDynamicResults(a.getDynamicResults(),\r
+ a.getMaxDynamicResults());\r
+ }\r
+ \r
+ resultsToWrap.close(); // Don't need the result set any more\r
+\r
+ // executeQuery() is not allowed if the statement\r
+ // doesn't return exactly one ResultSet.\r
+ if (executeQuery && dynamicResultCount != 1) {\r
+ throw StandardException.newException(\r
+ SQLState.LANG_INVALID_CALL_TO_EXECUTE_QUERY);\r
+ }\r
+\r
+ // executeUpdate() is not allowed if the statement\r
+ // returns ResultSets.\r
+ if (executeUpdate && dynamicResultCount > 0) {\r
+ throw StandardException.newException(\r
+ SQLState.LANG_INVALID_CALL_TO_EXECUTE_UPDATE);\r
+ }\r
+ \r
+ if (dynamicResultCount == 0) {\r
+ if (a.isSingleExecution()) {\r
+ a.close();\r
+ }\r
+\r
+ if (!forMetaData)\r
+ commitIfNeeded();\r
+ else {\r
+\r
+ if (lcc.getActivationCount() > 1) {\r
+ // we do not want to commit here as there seems to be other\r
+ // statements/resultSets currently opened for this connection.\r
+ } else {\r
+ commitIfNeeded(); // we can legitimately commit\r
+ }\r
+ }\r
+ }\r
+\r
+ retval = (dynamicResultCount > 0);\r
+ }\r
+ } catch (Throwable t) {\r
+ if (a.isSingleExecution()) {\r
+ try { a.close(); } catch (Throwable tt) {;}\r
+ }\r
+ throw handleException(t);\r
+ } finally {\r
+ restoreContextStack();\r
+ }\r
+ return retval;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Add a SQLWarning to this Statement object.\r
+ * If the Statement already has a SQLWarning then it\r
+ * is added to the end of the chain.\r
+ * \r
+ * @see #getWarnings()\r
+ */\r
+ final void addWarning(SQLWarning sw)\r
+ {\r
+ if (sw != null) {\r
+ if (warnings == null)\r
+ warnings = sw;\r
+ else\r
+ warnings.setNextException(sw);\r
+ }\r
+ }\r
+\r
+\r
+ /* package */\r
+ public String getSQLText()\r
+ {\r
+ // no need to synchronize - accessing a reference is atomic\r
+ // synchronized (getConnectionSynchronization()) \r
+ return SQLText;\r
+ }\r
+\r
+ public ParameterValueSet getParameterValueSet()\r
+ {\r
+ return pvs;\r
+ }\r
+\r
+ /**\r
+ * Throw an exception if this Statement has been closed explictly\r
+ * or it has noticed it has been closed implicitly.\r
+ * JDBC specifications require nearly all methods throw a SQLException\r
+ * if the Statement has been closed, thus most methods call this\r
+ * method or checkExecStatus first.\r
+ * \r
+ * @exception SQLException Thrown if the statement is marked as closed.\r
+ * \r
+ * @see #checkExecStatus()\r
+ */\r
+ final void checkStatus() throws SQLException {\r
+ if (!active) {\r
+ // \r
+ // Check the status of the connection first\r
+ //\r
+ java.sql.Connection appConn = getEmbedConnection().getApplicationConnection();\r
+ if (appConn == null || appConn.isClosed()) {\r
+ throw Util.noCurrentConnection();\r
+ }\r
+\r
+ throw newSQLException(SQLState.ALREADY_CLOSED, "Statement");\r
+ }\r
+ }\r
+\r
+ /**\r
+ A heavier weight version of checkStatus() that ensures the application's Connection\r
+ object is still open. This is to stop errors or unexpected behaviour when a [Prepared]Statement\r
+ object is used after the application has been closed. In particular to ensure that\r
+ a Statement obtained from a PooledConnection cannot be used after the application has closed\r
+ its connection (as the underlying Connection is still active).\r
+ To avoid this heavier weight check on every method of [Prepared]Statement it is only used\r
+ on those methods that would end up using the database's connection to read or modify data.\r
+ E.g. execute*(), but not setXXX, etc.\r
+ <BR>\r
+ If this Statement's Connection is closed an exception will\r
+ be thrown and the active field will be set to false,\r
+ completely marking the Statement as closed.\r
+ <BR>\r
+ If the Statement is not currently connected to an active\r
+ transaction, i.e. a suspended global transaction, then\r
+ this method will throw a SQLException but the Statement\r
+ will remain open. The Statement is open but unable to\r
+ process any new requests until its global transaction\r
+ is resumed.\r
+ <BR>\r
+ Upon return from the method, with or without a SQLException\r
+ the field active will correctly represent the open state of\r
+ the Statement.\r
+ \r
+ @exception SQLException Thrown if the statement is marked as closed\r
+ or the Statement's transaction is suspended.\r
+ \r
+ @see #checkStatus()\r
+ */\r
+ final void checkExecStatus() throws SQLException {\r
+ // getConnection() checks if the Statement is closed\r
+ if (!getConnection().isClosed())\r
+ return;\r
+ \r
+ // Now this connection is closed for all\r
+ // future use.\r
+ active = false;\r
+ \r
+ throw Util.noCurrentConnection();\r
+ }\r
+\r
+ /**\r
+ Close and clear all result sets associated with this statement\r
+ from the last execution.\r
+ */\r
+ void clearResultSets() throws SQLException {\r
+\r
+ SQLException sqle = null;\r
+\r
+ try {\r
+ // Are there results?\r
+ // outside of the lower try/finally since results will\r
+ // setup and restore themselves.\r
+ if (results != null) {\r
+ results.close();\r
+ results = null;\r
+ }\r
+ } catch (SQLException s1) {\r
+ sqle = s1;\r
+ }\r
+\r
+ try {\r
+ if (autoGeneratedKeysResultSet != null) {\r
+ autoGeneratedKeysResultSet.close();\r
+ autoGeneratedKeysResultSet = null;\r
+ }\r
+ } catch (SQLException sauto) {\r
+ if (sqle == null)\r
+ sqle = sauto;\r
+ else\r
+ sqle.setNextException(sauto);\r
+ }\r
+\r
+ // close all the dynamic result sets.\r
+ if (dynamicResults != null) {\r
+ for (int i = 0; i < dynamicResults.length; i++) {\r
+ EmbedResultSet lrs = dynamicResults[i];\r
+ if (lrs == null)\r
+ continue;\r
+\r
+ try {\r
+ lrs.close();\r
+ } catch (SQLException sdynamic) {\r
+ if (sqle == null)\r
+ sqle = sdynamic;\r
+ else\r
+ sqle.setNextException(sdynamic);\r
+ }\r
+ }\r
+ dynamicResults = null;\r
+ }\r
+\r
+ /*\r
+ We don't reset statement to null because PreparedStatement\r
+ relies on it being there for subsequent (post-close) execution\r
+ requests. There is no close method on database statement objects.\r
+ */\r
+\r
+ updateCount = -1; // reset field\r
+\r
+ if (sqle != null)\r
+ throw sqle;\r
+ } \r
+ \r
+ /**\r
+ Check to see if a statement requires to be executed via a callable statement.\r
+ */\r
+ void checkRequiresCallableStatement(Activation activation) throws SQLException {\r
+\r
+ ParameterValueSet pvs = activation.getParameterValueSet();\r
+\r
+ if (pvs == null)\r
+ return;\r
+\r
+ if (pvs.checkNoDeclaredOutputParameters()) {\r
+ try {\r
+ activation.close();\r
+ } catch (StandardException se) {\r
+ }\r
+ throw newSQLException(SQLState.REQUIRES_CALLABLE_STATEMENT, SQLText);\r
+ }\r
+ }\r
+\r
+ /**\r
+ Transfer my batch of Statements to a newly created Statement.\r
+ */\r
+ public void transferBatch(EmbedStatement other) throws SQLException {\r
+ \r
+ synchronized (getConnectionSynchronization()) {\r
+ other.batchStatements = batchStatements;\r
+ batchStatements = null;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Set the application statement for this Statement.\r
+ */\r
+ public final void setApplicationStatement(EngineStatement s) {\r
+ this.applicationStatement = s;\r
+ }\r
+\r
+ private EmbedResultSet[] dynamicResults;\r
+ private int currentDynamicResultSet;\r
+\r
+ /**\r
+ * Go through a holder of dynamic result sets, remove those that\r
+ * should not be returned, and sort the result sets according to\r
+ * their creation.\r
+ *\r
+ * @param holder a holder of dynamic result sets\r
+ * @param maxDynamicResultSets the maximum number of result sets\r
+ * to be returned\r
+ * @return the actual number of result sets\r
+ * @exception SQLException if an error occurs\r
+ */\r
+ private int processDynamicResults(java.sql.ResultSet[][] holder,\r
+ int maxDynamicResultSets)\r
+ throws SQLException\r
+ {\r
+\r
+ EmbedResultSet[] sorted = new EmbedResultSet[holder.length];\r
+\r
+ int actualCount = 0;\r
+ for (int i = 0; i < holder.length; i++) {\r
+\r
+ java.sql.ResultSet[] param = holder[i];\r
+\r
+ java.sql.ResultSet rs = param[0];\r
+\r
+ // Clear the JDBC dynamic ResultSet from the language\r
+ // ResultSet for the CALL statement. This stops the\r
+ // CALL statement closing the ResultSet when its language\r
+ // ResultSet is closed, which will happen just after the\r
+ // call to the processDynamicResults() method.\r
+ param[0] = null;\r
+ \r
+ // ignore non-Derby result sets or results sets from another connection\r
+ // and closed result sets.\r
+ EmbedResultSet lrs = EmbedStatement.processDynamicResult(\r
+ getEmbedConnection(), rs, this);\r
+ \r
+ if (lrs == null)\r
+ {\r
+ continue;\r
+ }\r
+\r
+ sorted[actualCount++] = lrs;\r
+ }\r
+\r
+ if (actualCount != 0) {\r
+\r
+ // results are defined to be ordered according to their creation\r
+ if (actualCount != 1) {\r
+ java.util.Arrays.sort(sorted, 0, actualCount);\r
+ }\r
+\r
+ dynamicResults = sorted;\r
+\r
+ if (actualCount > maxDynamicResultSets) {\r
+ addWarning(StandardException.newWarning(SQLState.LANG_TOO_MANY_DYNAMIC_RESULTS_RETURNED));\r
+\r
+ for (int i = maxDynamicResultSets; i < actualCount; i++) {\r
+ sorted[i].close();\r
+ sorted[i] = null;\r
+ }\r
+\r
+ actualCount = maxDynamicResultSets;\r
+ }\r
+\r
+\r
+ updateCount = -1;\r
+ results = sorted[0];\r
+ currentDynamicResultSet = 0;\r
+\r
+ // 0100C is not returned for procedures written in Java, from the SQL2003 spec.\r
+ // getWarnings(StandardException.newWarning(SQLState.LANG_DYNAMIC_RESULTS_RETURNED));\r
+ }\r
+\r
+\r
+ return actualCount;\r
+ }\r
+ \r
+ /**\r
+ * Process a ResultSet created in a Java procedure as a dynamic result.\r
+ * To be a valid dynamic result the ResultSet must be:\r
+ * <UL>\r
+ * <LI> From a Derby system\r
+ * <LI> From a nested connection of connection passed in\r
+ * or from the connection itself.\r
+ * <LI> Open\r
+ * </UL>\r
+ * Any invalid ResultSet is ignored.\r
+ * \r
+ * \r
+ * @param conn Connection ResultSet needs to belong to\r
+ * @param resultSet ResultSet to be tested\r
+ * @param callStatement Statement that executed the CALL, null if \r
+ * @return The result set cast down to EmbedResultSet, null if not a valid\r
+ * dynamic result.\r
+ */\r
+ static EmbedResultSet processDynamicResult(EmbedConnection conn,\r
+ java.sql.ResultSet resultSet,\r
+ EmbedStatement callStatement)\r
+ {\r
+ if (resultSet == null)\r
+ return null;\r
+\r
+ // ignore non-Derby result sets or results sets from another connection\r
+ if (!(resultSet instanceof EmbedResultSet))\r
+ return null;\r
+\r
+ EmbedResultSet lrs = (EmbedResultSet) resultSet;\r
+\r
+ if (lrs.getEmbedConnection().rootConnection != conn.rootConnection)\r
+ return null;\r
+\r
+ // ignore closed result sets.\r
+ try {\r
+ //following will check if the JDBC ResultSet or the language\r
+ //ResultSet is closed. If yes, then it will throw an exception.\r
+ //So, the exception indicates that the ResultSet is closed and\r
+ //hence we should ignore it. \r
+ lrs.checkIfClosed("");\r
+ } catch (SQLException ex) {\r
+ return null; \r
+ }\r
+ \r
+ lrs.setDynamicResultSet(callStatement);\r
+\r
+ return lrs;\r
+ }\r
+\r
+ /**\r
+ Callback on the statement when one of its result sets is closed.\r
+ This allows the statement to control when it completes and hence\r
+ when it commits in auto commit mode.\r
+\r
+ Must have connection synchronization and setupContextStack(), this\r
+ is required for the call to commitIfNeeded().\r
+ */\r
+ void resultSetClosing(EmbedResultSet closingLRS) throws SQLException {\r
+\r
+ // If the Connection is not in auto commit then this statement completion\r
+ // cannot cause a commit.\r
+ if (!getEmbedConnection().autoCommit)\r
+ return;\r
+\r
+ // If we have dynamic results, see if there is another result set open.\r
+ // If so, then no commit. The last result set to close will close the statement.\r
+ if (dynamicResults != null) {\r
+ for (int i = 0; i < dynamicResults.length; i++) {\r
+ EmbedResultSet lrs = dynamicResults[i];\r
+ if (lrs == null)\r
+ continue;\r
+ if (lrs.isClosed)\r
+ continue;\r
+ if (lrs == closingLRS)\r
+ continue;\r
+\r
+ // at least one still open so no commit now.\r
+ return;\r
+ }\r
+ }\r
+\r
+ // new Throwable("COMMIT ON " + SQLText).printStackTrace(System.out);\r
+\r
+ // beetle 5383. Force a commit in autocommit always. Before this\r
+ // change if client in autocommit opened a result set, did a commit,\r
+ // then next then close a commit would not be forced on the close.\r
+ commitIfAutoCommit();\r
+ }\r
+ \r
+ /**\r
+ * Get the execute time holdability for the Statement.\r
+ * When in a global transaction holdabilty defaults to false.\r
+ * @throws SQLException Error from getResultSetHoldability.\r
+ */\r
+ private boolean getExecuteHoldable() throws SQLException\r
+ {\r
+ if (resultSetHoldability == JDBC30Translation.CLOSE_CURSORS_AT_COMMIT)\r
+ return false;\r
+ \r
+ // Simple non-XA case\r
+ if (applicationStatement == this)\r
+ return true;\r
+ \r
+ return applicationStatement.getResultSetHoldability() ==\r
+ JDBC30Translation.HOLD_CURSORS_OVER_COMMIT;\r
+ }\r
+\r
+ /**\r
+ * Returns the value of the EmbedStatement's poolable hint,\r
+ * indicating whether pooling is requested.\r
+ *\r
+ * @return The value of the poolable hint.\r
+ * @throws SQLException if the Statement has been closed.\r
+ */\r
+\r
+ public boolean isPoolable() throws SQLException {\r
+ // Assert the statement is still active (not closed)\r
+ checkStatus();\r
+\r
+ return isPoolable;\r
+ } \r
+\r
+ /**\r
+ * Requests that an EmbedStatement be pooled or not.\r
+ *\r
+ * @param poolable requests that the EmbedStatement be pooled if true\r
+ * and not be pooled if false.\r
+ * @throws SQLException if the EmbedStatement has been closed.\r
+ */\r
+ \r
+ public void setPoolable(boolean poolable) throws SQLException {\r
+ // Assert the statement is still active (not closed)\r
+ checkStatus();\r
+\r
+ isPoolable = poolable;\r
+ }\r
+\r
+ public boolean isWrapperFor(Class<?> iface) throws SQLException {\r
+ // TODO Auto-generated method stub\r
+ return false;\r
+ }\r
+\r
+ public <T> T unwrap(Class<T> iface) throws SQLException {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
+\r
+ public void closeOnCompletion() throws SQLException {\r
+ // TODO Auto-generated method stub\r
+ \r
+ }\r
+\r
+ public boolean isCloseOnCompletion() throws SQLException {\r
+ // TODO Auto-generated method stub\r
+ return false;\r
+ }\r
+}\r
+\r