Package org.apache.derby.impl.jdbc

Source Code of org.apache.derby.impl.jdbc.EmbedStatement

/*

   Derby - Class org.apache.derby.impl.jdbc.EmbedStatement

   Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

package org.apache.derby.impl.jdbc;

import org.apache.derby.iapi.reference.JDBC20Translation;
import org.apache.derby.iapi.reference.JDBC30Translation;
import org.apache.derby.iapi.reference.SQLState;

import org.apache.derby.iapi.services.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 java.sql.SQLException;
import java.sql.SQLWarning;
import java.util.Vector;

/*
We would import these, but have name-overlap
import java.sql.Statement;
import java.sql.ResultSet;
*/

/**
*
* EmbedStatement is a local JDBC statement.
*
* @author ames
*/
public class EmbedStatement extends ConnectionChild
    implements java.sql.Statement {

  private final java.sql.Connection applicationConnection;

  protected int updateCount = -1;
  protected java.sql.ResultSet 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;
  protected String cursorName;

  protected final boolean forMetaData;
  public final int resultSetType;
  protected final int resultSetConcurrency;
  protected final int resultSetHoldability;
  protected final LanguageConnectionContext lcc;

  private SQLWarning warnings;
  protected String SQLText;

    private int fetchSize = 1;
    private int fetchDirection = JDBC20Translation.FETCH_FORWARD;
    int MaxFieldSize;

  //the state of this statement, set to false when close() is called
  protected 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
   protected Vector batchStatements;
 
  // The maximum # of rows to return per result set.
  // (0 means no limit.)
  int maxRows;

  private ParameterValueSet pvs;

  //
  // 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 = getEmbedConnection().getLanguageConnection();
    applicationConnection = getEmbedConnection().getApplicationConnection();
  }

  //
  // 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, JDBC30Translation.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
  {
    execute(sql, false, true, JDBC30Translation.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
  {
    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
  {
     throw Util.notImplemented("executeUpdate(String, int[])");
  }

    /**
     * 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
  {
     throw Util.notImplemented("executeUpdate(String, String[])");
  }

  protected 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);
  }

    /**
     * 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 resutl set
      clearResultSets();
     
      //next, release other resource
      cursorName = null;
      warnings = null;
      SQLText = null;
      batchStatements = null;
    }
  }

  // allow sub-classes to execute additional close
  // logic while holding the synchronization.
  protected 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, new Integer(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
  {
    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 
  {
    checkStatus();
    if (max < 0)
    {
      throw newSQLException(SQLState.INVALID_MAX_ROWS_VALUE, new Integer(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 int getQueryTimeout() throws SQLException {
        // Currently Cloudscape does not support any sort of timeout, so always
        // return 0, which means that timeout is unlimited.
        return(0);
  }

    /**
     * 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 void setQueryTimeout(int seconds) throws SQLException {
    if (seconds != 0)
      throw Util.notImplemented("setQueryTimeout");
  }

    /**
     * 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
   * @param executeQuery      caller is executeQuery()
   * @param executeUpdate      caller is executeUpdate()
   *
     * @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, JDBC30Translation.NO_GENERATED_KEYS, null, null);
  }
 
  protected boolean execute(String sql, boolean executeQuery, boolean executeUpdate,
    int autoGeneratedKeys, int[] columnIndexes, String[] columnNames) throws SQLException
  {

  // if sql is null, raise an error
  if (sql == null)
      throw newSQLException(SQLState.NULL_SQL_TEXT);

    synchronized (getConnectionSynchronization()) {

    checkExecStatus();
    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(sql);
        activation =
          preparedStatement.getActivation(lcc, resultSetType == JDBC20Translation.TYPE_SCROLL_INSENSITIVE);
        checkRequiresCallableStatement(activation);
       } 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 == JDBC30Translation.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 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 statement.
     *
     * @param sql any SQL statement
     * @param columnIndexes - an array of the indexes of the columns in the
     * inserted 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
  {
     throw Util.notImplemented("execute(String, int[])");
  }

    /**
     * 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 statement.
     *
     * @param sql any SQL statement
     * @param columnNames - an array of the names of the columns in the
     * inserted 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
  {
     throw Util.notImplemented("execute(String, String[])");
  }

    /**
     *  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 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(JDBC30Translation.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 == JDBC20Translation.FETCH_FORWARD ||
                    direction == JDBC20Translation.FETCH_REVERSE ||
                    direction == JDBC20Translation.FETCH_UNKNOWN )
                {
                    fetchDirection = direction;
                }else
                    throw newSQLException(SQLState.INVALID_FETCH_DIRECTION,
                                   new Integer(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 < || (this.getMaxRows() != 0 &&
                             rows > this.getMaxRows()))
        {
          throw newSQLException(SQLState.INVALID_ST_FETCH_SIZE, new Integer(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 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();
        batchStatements.addElement(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 {
    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 stmts = batchStatements;
      batchStatements = null;
      int size;
      if (stmts == null)
        size = 0;
      else
        size = stmts.size();

      int[] returnUpdateCountForBatch = new int[size];

      SQLException sqle;
      try {
        for (; i< size; i++)
        {
          if (executeBatchElement(stmts.elementAt(i)))
            throw newSQLException(SQLState.RESULTSET_RETURN_NOT_ALLOWED);
          returnUpdateCountForBatch[i] = getUpdateCount();
        }
        return returnUpdateCountForBatch;
      }
      catch (StandardException se) {

        sqle = handleException(se);
      }
      catch (SQLException sqle2)
      {
        sqle = sqle2;
      }
      finally
      {
        restoreContextStack();
      }

      int successfulUpdateCount[] = new int[i];
      for (int j=0; j<i; j++)
      {
        successfulUpdateCount[j] = returnUpdateCountForBatch[j];
      }

      SQLException batch =
      new java.sql.BatchUpdateException(sqle.getMessage(), sqle.getSQLState(),
                  sqle.getErrorCode(), successfulUpdateCount);

      batch.setNextException(sqle);
      throw batch;
      }
  }

  /**
    Execute a single element of the batch. Overridden by EmbedPreparedStatement
  */
  protected boolean executeBatchElement(Object batchElement) throws SQLException, StandardException {
    return execute((String)batchElement, false, true, JDBC30Translation.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();
        return false;
      }

      int startingClose;
      switch (current) {
      default:
      case JDBC30Translation.CLOSE_ALL_RESULTS:
        startingClose = 0;
        break;
      case JDBC30Translation.CLOSE_CURRENT_RESULT:
        // just close the current result set.
        startingClose = currentDynamicResultSet;
        break;
      case JDBC30Translation.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 {
          if (!lrs.isClosed)
            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 = -1;

      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  {
    if (autoGeneratedKeysResultSet == null)
      return null;
    else {
      execute("VALUES IDENTITY_VAL_LOCAL()", true, false, JDBC30Translation.NO_GENERATED_KEYS, null, null);
      return results;
    }
  }

  /////////////////////////////////////////////////////////////////////////
  //
  //  Implementation specific methods 
  //
  /////////////////////////////////////////////////////////////////////////

  /**
    Execute the current statement.
      @exception SQLException thrown on failure.
  */
  protected 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()) {
                        setupContextStack(); // make sure there's context
      boolean retval;

      pvs = a.getParameterValueSet();

      try {
        // The following is from the javadoc for java.sql.Statement
        // Only one ResultSet per Statement can be open at any point in time.
        // Therefore, if the reading of one ResultSet is interleaved with the
        // reading of another, each must have been generated by different Statements.
        // All statement execute methods implicitly close a
        // statment's current ResultSet if an open one exists.
        if (results != null) {
          results.close();
          results = null;
        }

        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();
          }
        }

        // if this was a prepared statement, this just
        // gets it for us, it won't recompile unless it is invalid.
        PreparedStatement ps = a.getPreparedStatement();
        ps.rePrepare(lcc);
        getWarnings(ps.getCompileTimeWarnings());


        /*
        ** 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);
        }
        a.setResultSetHoldability(resultSetHoldability != JDBC30Translation.CLOSE_CURSORS_AT_COMMIT);

        //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, executeQuery, executeUpdate, false);
        getWarnings(a.getWarnings());


        if (resultsToWrap.returnsRows()) {
          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.finalizeActivation = a;

          updateCount = -1;
          retval = true;
        }
        else {

          // Only applipable for an insert statement, which does not return rows.
          //the auto-generated keys resultset will be null if used for non-insert statement
          if (a.getAutoGeneratedKeysResultsetMode() && (resultsToWrap.getAutoGeneratedKeysResultset() != null))
          {
            resultsToWrap.getAutoGeneratedKeysResultset().open();
            autoGeneratedKeysResultSet = factory.newEmbedResultSet(getEmbedConnection(),
              resultsToWrap.getAutoGeneratedKeysResultset(), false, this, ps.isAtomic());
          }

          updateCount = resultsToWrap.modifiedRowCount();

          resultsToWrap.finish()// Don't need the result set any more
          results = null; // note that we have none.

          boolean haveDynamicResults = false;
          if (a.getDynamicResults() != null) {
            haveDynamicResults = processDynamicResults(a.getDynamicResults(), a.getMaxDynamicResults());
          }
         
          if (!haveDynamicResults) {
            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 = haveDynamicResults;
        }
          } catch (Throwable t) {
        if (a.isSingleExecution()) {
          try { a.close(); } catch (Throwable tt) {;}
        }
            throw handleException(t);
      } finally {
          restoreContextStack();
      }
      return retval;
    }
  }

  protected void getWarnings(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;
  }

  //check the status of this statement, if it has already been closed,
    //we throw an exception, need to be called by every public method
    protected final void checkStatus() throws SQLException {

    if (!active)
      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.
  */
  protected final void checkExecStatus() throws SQLException {
    // getConnection() checks if the Statement is closed
    if (!getConnection().isClosed())
      return;
 
    throw Util.noCurrentConnection();
  }

  /**
    Close and clear all result sets associated with this statement
    from the last execution.
  */
  protected void clearResultSets() 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 {
          if (!lrs.isClosed)
            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 = -1; // reset field

    if (sqle != null)
      throw sqle;
  
 
  /**
    Check to see if a statement requires to be executed via a callable statement.
  */
  protected 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;
    }
  }


  private EmbedResultSet[] dynamicResults;
  private int currentDynamicResultSet;
  private boolean 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];

      if (param[0] == null)
        continue;

      java.sql.ResultSet rs = param[0];
      param[0] = null;

      // ignore non-cloudscape result sets or results sets from another connection
      if (!(rs instanceof EmbedResultSet))
        continue;

      EmbedResultSet lrs = (EmbedResultSet) rs;

      if (lrs.getEmbedConnection().rootConnection != getEmbedConnection().rootConnection)
        continue;

      // ignore closed result sets.
      if (lrs.isClosed)
        continue;

      lrs.setDynamicResultSet(this);
      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) {
        getWarnings(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 = -1;
      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 true;
    }


    return false;
  }

  /**
    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();
  }

}
TOP

Related Classes of org.apache.derby.impl.jdbc.EmbedStatement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.