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