///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: AS400JDBCStatement.java
//
// The source code contained herein is licensed under the IBM Public License
// Version 1.0, which has been approved by the Open Source Initiative.
// Copyright (C) 1997-2010 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////

package com.ibm.as400.access;

import java.math.BigDecimal;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.Enumeration;
import java.util.Vector;



// @E6C - Changed javadoc paragraph about thread-safety.
/**
<p>The AS400JDBCStatement class provides a mechanism for
executing static SQL statements. Use Connection.createStatement()
to create new Statement objects.

<p>Only one result set per statement can be open at any point in time.
Therefore, if an application needs to read from multiple result sets,
then each must be generated by a different statement.

<p>AS400JDBCStatements are thread-safe.

<p>Note that the connection keeps a reference to each statement that
it creates.  This means that statements will not get garbage collected
until the connection gets garbage collected.  It is best to
explicitly close statements rather than counting on garbage collection.
**/
//
// Implementation notes:
//
// 1. The RPB is used to provide parameters for requests.
//    If we make a request without specifying some of the
//    parameters, then the system will look in the RPB.
//
//    Specifically, we use an RPB for each statement object to
//    store parameters that do not depend on the SQL statement:
//
//    * cursor name
//    * statement name
//
// 2. There are many package-scope members in this class
//    heirarchy which really should be "private protected".
//    However, that is no longer allowed in Java, so we
//    at least limit access out the the package.  They
//    are not intended to be used outside of the class
//    hierarchy.
//
public class AS400JDBCStatement
/* ifdef JDBC40
extends ToolboxWrapper
endif */
implements Statement
{
    static final String copyright = "Copyright (C) 1997-2003 International Business Machines Corporation and others.";


    // Constants.
    static final int            MAX_CURSOR_NAME_LENGTH_PRE_V6R1 = 18;   //@550C
    static final int            MAX_CURSOR_NAME_LENGTH          = 128;  //@550A

    // Constants for generated key support
    static final int            RETURN_GENERATED_KEYS = 1;    //@G4A
    static final int            NO_GENERATED_KEYS = 2;    //@G4A
    static final int            GENERATED_KEYS_NOT_SPECIFIED = -9999;    //@G4A


    // Private data.
    boolean                 allowImmediate_;    // private protected
    private     int         autoGeneratedKeys_;    // @G4A
    Vector                  batch_;    // private protected
    private     String                  blockCriteria_;
    private     int                     blockSize_;
    private     boolean                 cancelled_;
    private     boolean                 closed_;
    private     boolean                 closeOnCompletion_;  //@D7A
    protected AS400JDBCConnection     connection_;    // private protected
    JDCursor                cursor_;    // private protected
    private     String                  cursorDefaultName_;
    private     boolean                 escapeProcessing_;
    protected     DBExtendedColumnDescriptors  extendedColumnDescriptors_;    // @F3A@P6C
    private     int                     fetchDirection_;
    private     int                     fetchSize_;
    private     AS400JDBCResultSet      generatedKeys_;    // @G4A
    int                     id_;    // private protected
    AS400JDBCStatementLock              internalLock_;    // private protected    // @E6A@C7C
    private     boolean                 lastPrepareContainsLocator_;    // @B2A
    private     int                     maxFieldSize_;
    private     int                     maxRows_;
    private     long                    longMaxRows_;
    int                                 rowsInserted_;    // for block insert @G5A
    private     String                  name_;
    private     String                  nameOverride_;
    int                     numberOfResults_;    // private protected
    int                     positionOfSyntaxError_;    //@F10A
    boolean                 prefetch_;    // private protected
    int                     queryTimeout_;                /*@D4A*/
    private     boolean                 queryTimeoutSet_ = false;  /*@B2A*/
    AS400JDBCResultSet      resultSet_;    // private protected
    private     int                     rowCountEstimate_;    // @ECA
    private     boolean                 rpbCreated_;
    private     boolean                 rpbQueryTimeoutChanged_;    //@EFA
    private     boolean                 rpbSyncNeeded_;
    JDPackageManager        packageManager_;    // private protected
    int                     resultSetConcurrency_;    // private protected
    int                     resultSetHoldability_;    //@G4A
    private     int                     resultSetType_;
    SQLConversionSettings   settings_;    // private protected
    private     SQLWarning              sqlWarning_;
    JDTransactionManager    transactionManager_;    // private protected
    int                     updateCount_;    // private protected
    private     String      packageCriteria_;    // @A1A
    int                     behaviorOverride_ = 0;    // @F9a
    private boolean     associatedWithLocators_ = false;        //@KBL set to true, if this statement was used to access a locator
    private boolean     holdStatement_ = false;                //@KBL set to true, if rpb and ors for this statement should be left open until a transaction boundary
    protected boolean     useVariableFieldCompression_ = false;    //@K54  does connection allow compression
    protected boolean     useVariableFieldInsertCompression_ = false;    //@K54 does connection allow compressions
    private boolean     isPoolable_ = false;         //@PDA  jdbc40
    JDServerRow         parameterRow_;          // private protected //@re-prep moved from preparedStatement so that it has visibility here
    private boolean     threadInterrupted = false;
    private DBReplyRequestedDS commonExecuteReply = null; // reply from commonExecute.  Note:  This cannot be returned to the pool until it is
                                                          // no longer being used.  The data_ pointer from this reply is shared with
                                                          // all kinds of objects.
    	                                                  // Note:  Because of the current structure, this can be returned after it is used.  The data_
                                                          // pointer from the reply was created by ClientAccessDataStream.construct.  The returnToPool
                                                          // method for the reply has been corrected to set the data_ pointer back to DBStorage_.data.
                                                          // @B5A
    private DBReplyRequestedDS connectReply = null;
    private DBReplyRequestedDS execImmediateReply = null;
    private DBReplyRequestedDS normalPrepareReply = null;
    private DBReplyRequestedDS getMoreResultsReply = null;

    protected boolean queryRunning_;    // Used to determine whether or not we need to track}
                                        // a QueryCancelThread.  @D4A
    protected AS400JDBCQueryCancelThread cancelThread_;    /*@D4A*/


    protected boolean disableRllCompression_ = false; //@L9A 

    JDSQLStatement currentJDSQLStatement_ = null;


     Exception creationLocation_;
    
    /**
    Constructs an AS400JDBCStatement object.

    @param  connection              The connection to the system.
    @param  id                      The id.
    @param  transactionManager      The transaction manager for the connection.
    @param  packageManager          The package manager for the connection.
    @param  blockCriteria           The block criteria.
    @param  blockSize               The block size (in KB).
    @param  prefetch                Indicates if prefetching data.
    @param  packageCriteria         The package criteria.
    @param  resultSetType           The result set type.
    @param  resultSetConcurrency    The result set concurrency.
    @param  resultSetHoldability    The result set holdability.
    @param  autoGeneratedKeys       Indicates if auto generated keys were requested.

    @exception  SQLException    If an error occurs.
    **/
    AS400JDBCStatement (AS400JDBCConnection connection,
                        int id,
                        JDTransactionManager transactionManager,
                        JDPackageManager packageManager,
                        String blockCriteria,
                        int blockSize,
                        boolean prefetch,
                        String packageCriteria,    // @A1C
                        int resultSetType,
                        int resultSetConcurrency,
                        int resultSetHoldability,    // @G4A
                        int autoGeneratedKeys)    // @G4A
    throws SQLException
    {
        // Initialization.
        allowImmediate_         = true;
        autoGeneratedKeys_      = autoGeneratedKeys;    // @G4A
        //@P0Dbatch_                  = new Vector ();
        blockCriteria_          = blockCriteria;
        blockSize_              = blockSize;
        cancelled_              = false;
        closed_                 = false;
        connection_             = connection;
        escapeProcessing_       = true;
        fetchDirection_         = ResultSet.FETCH_FORWARD;
        fetchSize_              = 0;
        id_                     = id;
        internalLock_           = new AS400JDBCStatementLock();    // @E6A
        maxFieldSize_           = 0;
        maxRows_                = 0;
        longMaxRows_            = 0; 
        numberOfResults_        = 0;
        prefetch_               = prefetch;
        queryTimeout_           = 0;
        resultSet_              = null;
        transactionManager_     = transactionManager;
        updateCount_            = -1;
        packageManager_         = packageManager;
        resultSetConcurrency_   = resultSetConcurrency;
        resultSetHoldability_   = resultSetHoldability;    //@G4A
        resultSetType_          = resultSetType;
        rpbCreated_             = false;
        rpbQueryTimeoutChanged_ = false;    //@EFA
        rpbSyncNeeded_          = true;
        settings_               = SQLConversionSettings.getConversionSettings (connection_);
        sqlWarning_             = null;
        packageCriteria_        = packageCriteria;    // @A1A

        // By default, the statement name and cursor name are
        // based on the id.
        String idString     = "0000" + id;
        String idString4 = idString.substring (idString.length() - 4);
        name_ = "STMT" + idString4;

        useVariableFieldInsertCompression_ = connection_.useVariableFieldInsertCompression();
        if(resultSetType_ == ResultSet.TYPE_FORWARD_ONLY)    // @B1A
        {
            cursorDefaultName_ = "CRSR" + idString4;
            if(connection_.useVariableFieldCompression())   //@K54
               useVariableFieldCompression_ = true;                                                                                                   //@K54
        }
        else    // @B1A
            cursorDefaultName_ = "SCRSR" + idString4;    // @B1A

        // @E4D // Use lazy close only when the "lazy close" property                                      @E4A
        // @E4D // has been set AND the old auto-commit support is                                         @E4A
        // @E4D // still in effect.                                                                        @E4A
        // @E4D boolean lazyClose = connection_.getProperties().getBoolean(JDProperties.LAZY_CLOSE)     // @E4A
        // @E4D                     && transactionManager_.isNewAutoCommitSupport();                    // @E4A

        cursor_ = new JDCursor (connection_, id_, cursorDefaultName_, resultSetConcurrency_);    // @E4C @EAC

        //@F4 If the user specified a holdability, then we must check the holdability of
        //@F4 statements on a Connection.commit or Connection.rollback.  Thought about just
        //@F4 setting this flag to true if the holdability was different than the connection
        //@F4 holdability, but the connection holdability can change above us.
        if(resultSetHoldability != AS400JDBCResultSet.HOLDABILITY_NOT_SPECIFIED)    //@F4A
        {
            //@F4A
            connection.setCheckStatementHoldability(true);    //@F4A
        }    //@F4A

        try    // @F9a
        {
            // @F9a
            behaviorOverride_ = connection_.getProperties().getInt(JDProperties.BEHAVIOR_OVERRIDE);    // @F9a
        }    // @F9a
        catch(Throwable t)
        {
        }    // @F9a

        // Trace messages.
        if(JDTrace.isTraceOn())
        {
            JDTrace.logOpen (this, connection_);    // @J33c
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Escape processing", escapeProcessing_);
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Fetch direction", fetchDirection_);
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Fetch size", fetchSize_);
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Max field size", maxFieldSize_);
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Max rows", maxRows_);
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Query timeout", queryTimeout_);
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Result set concurrency", resultSetConcurrency_);
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Result set holdability", resultSetHoldability_);    //@F4A
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Result set type", resultSetType_);
            JDTrace.logProperty (this, "AS400JDBCStatement.init", "Behavior Override", behaviorOverride_);    // @F9a
            String cursorAsString = JDTrace.objectToString(cursor_);    // @J33a
            JDTrace.logInformation(this, "Data to correlate statement with cursor " + cursorAsString);    // @J33a
            creationLocation_ = new Exception("creationLocation_"); 
        }
    }



    // JDBC 2.0
    /**
    Adds an SQL statement to the current batch of SQL statements.

    @param sql  The SQL statement to be added to the current batch.
                This can be any SQL statement that does not return
                a result set.

    @exception SQLException     If the statement is not open or
                                the SQL statement contains a syntax
                                error.
    **/
    public void addBatch (String sql)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            JDSQLStatement sqlStatement = new JDSQLStatement (sql,
                                                              settings_.getDecimalSeparator (), escapeProcessing_,
                                                              packageCriteria_, connection_);    //@G4C
            currentJDSQLStatement_ = sqlStatement; 
            // Validate that no parameters are set, since parameters
            // imply the need for a PreparedStatement.
            if(sqlStatement.countParameters () > 0)
                JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);

            if(batch_ == null) batch_ = new Vector();    //@P0A
            batch_.addElement (sqlStatement);
        }
    }



    /**
    Cancels the statement.  This is useful when one thread
    needs to cancel a statement that is being executed by another
    thread.  This will close the current result set.

    @exception  SQLException    If the statement is not open or
                                an error occurs.
    **/
    public void cancel ()
    throws SQLException
    {
        // Don't synchronize this method... it needs to be callable             // @E6A
        // even when the statement is busy.                                     // @E6A
        checkOpen ();

        connection_.cancel(id_);    // @E5A

        cancelled_ = true;
        closeResultSet (JDCursor.REUSE_YES);
    }



    /**
    Checks that the statement is open.  Public methods
    that require an open statement should call this first.

    @exception  SQLException    If the statement is not open.
    **/
    void checkOpen ()
    throws SQLException
    {
        connection_.checkOpen ();
        if(closed_)
            JDError.throwSQLException (JDError.EXC_FUNCTION_SEQUENCE);
    }



    // JDBC 2.0
    /**
    Clears the current batch of SQL statements.

    @exception SQLException     If the statement is not open.
    **/
    public void clearBatch ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            if(batch_ == null) batch_ = new Vector();    //@P0A
            batch_.removeAllElements ();
        }
    }



    /**
    Clears all warnings that have been reported for the statement.
    After this call, getWarnings() returns null until a new warning
    is reported for the statement.

    @exception SQLException If an error occurs.
    **/
    public void clearWarnings ()
    throws SQLException
    {
        sqlWarning_ = null;
    }



    /**
    Releases the statement's resources immediately instead of waiting
    for them to be automatically released.  This closes the current
    result set.

    @exception SQLException If an error occurs.
    **/
    //
    // Implementation note:
    //
    // It is a requirement to not get replies during a finalize()
    // method.  Since finalize() calls this method, this requirement
    // applies here, too.
    //
    public void close ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            // If this is already closed, then just do nothing.
            //
            // The spec does not define what happens when a connection
            // is closed multiple times.  The official word from the Sun
            // JDBC team is that "the driver's behavior in this case
            // is implementation defined.   Applications that do this are
            // non-portable."
            if(isClosed ())
                return;

            // Close the current result set.  (Note: This has no
            // effect if the user explicitly closed the result set
            // before closing the statement.
            //@PDA perf2 comment:  if we get back 700,2 from fetch, then cursor is already closed, but resultSet_ is still usable
            //if(! cursor_.isClosed())   //@perf3 cursor can be closed, but resultset still needs to be closed.  closeResultSet checks for closed cursor before closing cursor.
            //{    // @B3A               //@perf3
            closeResultSet (JDCursor.REUSE_NO);
            //}    // @B3A               //@perf3

            // If, even after closing the current result set,
            // there are more result sets that were returned, we
            // need to close them, too.  At first I though we
            // would have to Open/Describe and Close each one
            // in turn, but the database host server allows us to
            // close the current cursor with REUSE_NO to close
            // all remaining result sets.  The catch is that we
            // need to have an open cursor before closing it, so
            // we open the next cursor, then close it again.
            // This closes all remaining result sets.
            if(numberOfResults_ > 1)
            {
                getMoreResults ();
                cursor_.close (JDCursor.REUSE_NO);
            }

            if(generatedKeys_ != null)    //@PDA genkeys
            {
                generatedKeys_.close();
                generatedKeys_ = null;
            }

            //@KBL If the user specified to keep statements open until a transaction boundary and locators are associated
            // with the statement, do not close the RPB or ORS until a transaction boundary is reached.
            if(isAssociatedWithLocators() && (connection_.getProperties().getBoolean(JDProperties.HOLD_STATEMENTS)) && (connection_.getAutoCommit() == false))
            {
                setHoldStatement(true);
                closed_ = true;     // Want the statement to only be available for use internally, user should not be able to use the statement.

                if (commonExecuteReply != null)  { commonExecuteReply.returnToPool(); commonExecuteReply = null; }
                if (connectReply != null)        { connectReply.returnToPool(); connectReply = null; }
                if (execImmediateReply != null)  { execImmediateReply.returnToPool(); execImmediateReply = null; }
                if (normalPrepareReply != null)  { normalPrepareReply.returnToPool(); normalPrepareReply = null; }
                if (getMoreResultsReply != null) { getMoreResultsReply.returnToPool(); getMoreResultsReply = null; }
                return;             // Mark the statement as closed, but don't notify the connection it has been closed or delete the RPB and ORS.
            }

            // Delete the RPB.  Remember the error information                                             @EDC
            // in order to report later...                                                                 @EDC
            SQLException e = null;    // @EDA
            if(rpbCreated_)
            {    // @EDA
                DBSQLRPBDS request3 = null;    //@P0A
                DBReplyRequestedDS closeReply = null;    //@P0A
                try
                {    //@P0A
                    request3 = DBDSPool.getDBSQLRPBDS(DBSQLRPBDS.FUNCTIONID_DELETE_RPB, id_, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA, 0);    //@P0C

                    closeReply = connection_.sendAndReceive(request3, id_);    // @EDC @P0C

                    int errorClass = closeReply.getErrorClass();    // @EDA
                    int returnCode = closeReply.getReturnCode();    // @EDA
                    if(errorClass != 0)
                    {    // @EDA
                        if(returnCode < 0)
                        {    // @EDA
                            try
                            {    // @EDA
                                JDError.throwSQLException(this, connection_, id_, errorClass, returnCode);    // @EDA
                            }    // @EDA
                            catch(SQLException e2)
                            {    // @EDA
                                e = e2;    // @EDA
                            }    // @EDA
                        }    // @EDA
                        else
                        {    // @EDA
                            postWarning (JDError.getSQLWarning(connection_, id_, errorClass, returnCode));    // @EDA
                        }    // @EDA
                    }    // @EDA
                }
                finally
                {    //@P0A
                    if(request3 != null) {
                    		request3.returnToPool();  request3=null;
                    }
                    if(closeReply != null) {
                    	closeReply.returnToPool();closeReply=null;
                    }
                }
            }    // @EDA

            // Delete the ORS.
            //
            // @EDD // We must use get a reply here even though we do not
            // @EDD // need any information from it.  Otherwise the next
            // @EDD // flow would be based on this ORS, which gets deleted.
            // @EDD // In that case, we would always get an "ORS not found"
            // @EDD // error.
            // @EDD //
            // @EDD // In this case, we also need it to force a send of
            // @EDD // all of the previous datastreams, since this is the
            // @EDD // last one for this statement.
            //
            // Make sure not to base future requests on this ORS.                                   // @EDA
            //
            //@P0DDBSQLResultSetDS request2 = new DBSQLResultSetDS (
            //@P0D                                                 DBSQLResultSetDS.FUNCTIONID_DELETE_RESULTS_SET,
            //@P0D                                                 id_, 0, 0);                                                                         // @EDC
            DBSQLResultSetDS request2 = null;    //@P0A
            try
            {    //@P0A
                request2 = DBDSPool.getDBSQLResultSetDS(DBSQLResultSetDS.FUNCTIONID_DELETE_RESULTS_SET, id_, 0, 0);    //@P0C

                connection_.send(request2, id_, false);    // @EDC
            }
            finally
            {    //@P0A
                if(request2 != null) {
                	request2.returnToPool();  request2=null;
                }
            }

            // Ignore errors, since we would not be able to get
            // the message text anyway (because the ORS is gone.)

            // free the pooled commonExecuteReply
            if (commonExecuteReply != null)  { commonExecuteReply.returnToPool(); commonExecuteReply = null; }
            if (connectReply != null)        { connectReply.returnToPool(); connectReply = null; }
            if (execImmediateReply != null)  { execImmediateReply.returnToPool(); execImmediateReply = null; }
            if (normalPrepareReply != null)  { normalPrepareReply.returnToPool(); normalPrepareReply = null; }
            if (getMoreResultsReply != null) { getMoreResultsReply.returnToPool(); getMoreResultsReply = null; }
            closed_ = true;
            connection_.notifyClose (this, id_);

            if(JDTrace.isTraceOn())
                JDTrace.logClose (this);

            // Rethrow any exception that surfaced when deleting the RPB.                              @EDA
            if(e != null)    // @EDA
                throw e;    // @EDA
        }
    }

    //@KBL
    /*
    Finish releasing a partially-closed statement's resources.  A statement may become partially closed if, for example,
    the user called close on the statement and it was associated with a locator.
    This method will delete any RPB and ORS associated with the statement.".
    @exception SQLException if an error occurs
    */
    void finishClosing()
    throws SQLException
    {
        synchronized(internalLock_)
        {
            // Delete the RPB.  Remember the error information
            // in order to report later...
            SQLException e = null;
            if(rpbCreated_)
            {
                DBSQLRPBDS request3 = null;
                DBReplyRequestedDS finishClosingReply = null;
                try
                {
                    request3 = DBDSPool.getDBSQLRPBDS(DBSQLRPBDS.FUNCTIONID_DELETE_RPB, id_, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA, 0);    //@P0C

                    finishClosingReply = connection_.sendAndReceive(request3, id_);

                    int errorClass = finishClosingReply.getErrorClass();
                    int returnCode = finishClosingReply.getReturnCode();
                    if(errorClass != 0)
                    {
                        if(returnCode < 0)
                        {
                            try
                            {
                                JDError.throwSQLException(this, connection_, id_, errorClass, returnCode);
                            }
                            catch(SQLException e2)
                            {
                                e = e2;
                            }
                        }
                        else
                        {
                            postWarning (JDError.getSQLWarning(connection_, id_, errorClass, returnCode));
                        }
                    }
                }
                finally
                {
                    if(request3 != null) { request3.returnToPool(); request3=null; }
                    if(finishClosingReply != null) { finishClosingReply.returnToPool(); finishClosingReply = null; }
                }
            }

            // Delete the ORS.
            //
            // We must use get a reply here even though we do not
            // need any information from it.  Otherwise the next
            // flow would be based on this ORS, which gets deleted.
            // In that case, we would always get an "ORS not found"
            // error.
            // In this case, we also need it to force a send of
            // all of the previous datastreams, since this is the
            // last one for this statement.
            //
            // Make sure not to base future requests on this ORS.
            DBSQLResultSetDS request2 = null;
            try
            {
                request2 = DBDSPool.getDBSQLResultSetDS(DBSQLResultSetDS.FUNCTIONID_DELETE_RESULTS_SET, id_, 0, 0);

                connection_.send(request2, id_, false);
            }
            finally
            {
                if(request2 != null) {
                		request2.returnToPool();   request2 = null;
                }
            }

            // Ignore errors, since we would not be able to get
            // the message text anyway (because the ORS is gone.)

            closed_ = true;
            setHoldStatement(false);                //the statement is no longer left open
            connection_.notifyClose (this, id_);

            if(JDTrace.isTraceOn())
                JDTrace.logClose (this);

            // Rethrow any exception that surfaced when deleting the RPB.
            if(e != null)
                throw e;
        }

    }

    /**
    Closes the result set and cursor.

    @param      reuseFlag   Either JDCursor.REUSE_NO,
                            JDCursor.REUSE_YES, or
                            JDCursor.REUSE_RESULT_SET.

    @exception  SQLException    If an error occurs.
    **/
    void closeResultSet (int reuseFlag)    // private protected
    throws SQLException
    {
        if(resultSet_ != null)
        {
            if(! resultSet_.isClosed ())
                resultSet_.close ();
            resultSet_ = null;
        }

        if (threadInterrupted) {
        	// Force a close to be flowed
        	try {
        		cursor_.setState(false);
        		cursor_.close (reuseFlag);
        	} catch (Exception e) {

        	}
        } else {
           if(! cursor_.isClosed ())
              cursor_.close (reuseFlag);
        }

        updateCount_ = -1;
    }



    /**
    Executes an SQL statement on the IBM i system.

    @param  sqlStatement    The SQL statement.
    @param  resultRow       The result row or null if none.

    @exception      SQLException    If the query timeout limit is
                                    exceeded or an error occurs.
    **/
    //
    // Do not override this method.  Instead, override
    // commonExecuteBefore() and commonExecuteAfter() as needed.
    //
    final void commonExecute (JDSQLStatement sqlStatement,
                              JDServerRow resultRow)    // private protected
    throws SQLException
    {
      SQLException savedException = null;   /*@F3A*/
      currentJDSQLStatement_ = sqlStatement; 

      try {
        cancelled_ = false;

        /*@D4A*/
        if (connection_.isQueryTimeoutMechanismCancel()) {
          startCancelThread();
        }

        // If the statement is not immediately executable, then
        // we still need to do the execute.  Otherwise, the execute
        // was already done as part of the prepare.
        if((! allowImmediate_) || (! sqlStatement.isImmediatelyExecutable ()))
        {



          //
          // If using generated keys, make sure that the prefetch
          // operation is disabled.  If the prefetch option is not
          // disabled, then it is not possible to the the updated row
          // count from the insert.
          // @E5
          if (autoGeneratedKeys_ == RETURN_GENERATED_KEYS && prefetch_) {
            prefetch_ = false;
          }

          // Also close the autogenerated key if it is open.
          // Breaking the exec into two pieces causes this to be closed
          // right after the exec if it already is open
          // @E5
          if (generatedKeys_ != null) {
            if (!generatedKeys_.isClosed()) {
              try {
              generatedKeys_.close();
              } catch (Exception e) {

              }
            }
          }

            // Sync up the RPB.
            syncRPB ();

            boolean usedNameOverride = false;

            // This boolean is true when an Open is needed and
            // false when an Execute is needed.
            boolean openNeeded = (resultRow != null);

            // If we are prefetching data and a row format
            // was returned, then fetch the first block of data.
            boolean fetchFirstBlock = ((prefetch_) && (openNeeded));

            // Send the execute data stream.
            try
            {
                // Determine the appropriate function id, based
                // on whether we want to prefetch data, and on whether
                // this is an Execute or an Open.
                int functionId;
                if(openNeeded)
                {
                    if(fetchFirstBlock)
                        functionId = DBSQLRequestDS.FUNCTIONID_OPEN_DESCRIBE_FETCH;
                    else
                        functionId = DBSQLRequestDS.FUNCTIONID_OPEN_DESCRIBE;
                }
                else
                    functionId = DBSQLRequestDS.FUNCTIONID_EXECUTE;

                DBSQLRequestDS request = null;    //@P0A
                // DBReplyRequestedDS replyX = null;    //@P0A
                int openAttributes = 0;
                try
                {
                    //@541 When running to a V5R4 or higher system, we can request to get extended column descriptors
                    // from a stored procedure result set.  In order to do that, we need to set the extended column
                    // descriptor option on the execute of the statement.
                    boolean isCall = (sqlStatement.getNativeType () == JDSQLStatement.TYPE_CALL);       //@541A moved up from farther below in code
                    //@F5D Send this on the prepare, not the execute
                    int requestedORS = DBSQLRequestDS.ORS_BITMAP_RETURN_DATA+DBSQLRequestDS.ORS_BITMAP_SQLCA;      //@F3M   //@541C undeleted
                    //@F5D //@F3A If we are on a system that supports extended column descriptors and if the              //@F3A
                    //@F5D //@F3A user asked for them, send the extended column descriptors code point.                   //@F3A
                    boolean extendedMetaData = false;                                                              //@F3A   //@541C undeleted
                    //@F5D if (connection_.getVRM() >= JDUtilities.vrm520)                                                //@F3A
                    if(connection_.getVRM() >= JDUtilities.vrm540 && isCall)                                        //@541A
                    {                                                                                              //@F3A   //@541C  undeleted
                         extendedMetaData = connection_.getProperties().getBoolean(JDProperties.EXTENDED_METADATA); //@F3A  //@541C  undeleted
                         if (extendedMetaData)                                                                      //@F3A  //@541C  undeleted
                         {                                                                                          //@F3A  //@541C  undeleted
                            requestedORS = requestedORS + DBSQLRequestDS.ORS_BITMAP_EXTENDED_COLUMN_DESCRIPTORS;    //@F3A  //@541C  undeleted
                         }                                                                                          //@F3A  //@541C  undeleted
                    }                                                                                              //@F3A   //@541C  undeleted
                    if(connection_.getVRM() >= JDUtilities.vrm610 )  {   //@cur request cursor attributes  //@isol
                        requestedORS = requestedORS + DBSQLRequestDS.ORS_BITMAP_CURSOR_ATTRIBUTES; //@cur
                    }
                    //@P0A
                    request = DBDSPool.getDBSQLRequestDS(functionId, id_, requestedORS, 0);    //@P0C @F3C @F5C //@541C

                    openAttributes = cursor_.getOpenAttributes(sqlStatement, blockCriteria_);    //@F7M
                    if(openNeeded)
                    {
                        //@F7D openAttributes = cursor_.getOpenAttributes (sqlStatement, blockCriteria_);
                        if((autoGeneratedKeys_ == RETURN_GENERATED_KEYS) && connection_.getVRM() >= JDUtilities.vrm610) //@GKA Wrapped INSERT WITH SELECT for generated keys.  Result set should be read only, insensitive
                            request.setOpenAttributes(0x80);    //@GKA
                        else                                    //@GKA
                            request.setOpenAttributes(openAttributes);
                    }
                    else if(extendedMetaData){      //@541A Doing an execute.  If running to V5R4 and higher, and the extendedMetaData property is true, set the extended column descriptor option
                        request.setExtendedColumnDescriptorOption((byte)0xF1);
                    }

                    if(nameOverride_.length() != 0)
                    {
                        request.setPrepareStatementName(nameOverride_, connection_.converter_);    //@P0C
                        usedNameOverride = true;
                    }

                    if(packageManager_.isEnabled() && sqlStatement.isPackaged())
                    {
                        request.setPackageName (packageManager_.getName (), connection_.converter_);    //@P0C
                    }

                    String cursorSensitivity = connection_.getProperties().getString(JDProperties.CURSOR_SENSITIVITY);    //@H1A

                    // If we are prefetching data and a row format
                    // was returned, then set the blocking factor if a specific number of rows have been asked for
                    // or variable-length field compression is turned off, otherwise set the buffer size.
                    if(fetchFirstBlock)
                    {
                        request.addOperationResultBitmap(DBSQLRequestDS.ORS_BITMAP_RESULT_DATA);
                        if((functionId == DBSQLRequestDS.FUNCTIONID_OPEN_DESCRIBE_FETCH) &&                     //@K54
                           (useVariableFieldCompression_))                                                       //@K54
                        {
                            //Do not need to set the blocking factor if using variable-length field compression
                            //If both the buffer size and blocking factor were set, the buffer size will override
                            //the blocking factor and the number of rows that will fit in the buffer size will be returned
                            //regardless of the blocking factor value
                            request.setVariableFieldCompression(true);
                            request.setBufferSize(blockSize_ * 1024);
                        }
                        else {                                                    //@K54
                          
                        	if (resultRow != null) {  // @B5A -- check for null pointer
                               request.setBlockingFactor(getBlockingFactor (cursorSensitivity, sqlStatement, resultRow.getRowLength()));    //@K54 changed to just use resultRow.getRowLength() instead of fetchFirstBlock ? resultRow.getRowLength() : 0
                        	} else {
                                request.setBlockingFactor(getBlockingFactor (cursorSensitivity, sqlStatement, 0));    //@K54 changed to just use resultRow.getRowLength() instead of fetchFirstBlock ? resultRow.getRowLength() : 0
                        	}
                        }
                    }

                    //@K1D //@F8 If we are pre-V5R2, the user set the resultSetType to "TYPE_SCROLL_INSENSITIVE", or
                    //@K1D //@F8 the user did not change from the default of the "cursor sensitivity" property,
                    //@K1D //@F8 send what we always have.
                    //@K1D //@F8 Change in a future release to send CURSOR_SCROLLABLE_INSENSITIVE and
                    //@K1D //@F8 CURSOR_NOT_SCROLLABLE_INSENSITIVE if resultSetType_ ==
                    //@K1D //@F8 ResultSet.TYPE_SCROLL_INSENSITIVE to v5r1 or later hosts.
                    // String cursorSensitivity = connection_.getProperties().getString(JDProperties.CURSOR_SENSITIVITY);    //@F8A
                    //@K1D if((connection_.getVRM() < JDUtilities.vrm520)                                                    //@F8A
                    //@K1D    || (resultSetType_ == ResultSet.TYPE_SCROLL_INSENSITIVE)                                       //@F8A
                    //@K1D    || (cursorSensitivity.equalsIgnoreCase(JDProperties.CURSOR_SENSITIVITY_ASENSITIVE)))           //@F8A
                    //@K1D {
                    //@K1D     if(resultSetType_ == ResultSet.TYPE_FORWARD_ONLY)
                    //@K1D         request.setScrollableCursorFlag (DBSQLRequestDS.CURSOR_NOT_SCROLLABLE_ASENSITIVE);
                    //@K1D     else
                    //@K1D         request.setScrollableCursorFlag (DBSQLRequestDS.CURSOR_SCROLLABLE_ASENSITIVE);
                    //@K1D }
                    //@K1D //@F8 If we are V5R2 or later, send new numbers based on what the user
                    //@K1D //@F8 set in "cursor sensitivity" property                                                      //@F8A
                    //@K1D else if(resultSetType_ == ResultSet.TYPE_FORWARD_ONLY)
                    //@K1D {
                    //@K1D     if(cursorSensitivity.equalsIgnoreCase(JDProperties.CURSOR_SENSITIVITY_INSENSITIVE))     //@F8A
                    //@K1D         request.setScrollableCursorFlag (DBSQLRequestDS.CURSOR_NOT_SCROLLABLE_INSENSITIVE);  //@F8A
                    //@K1D     else  //else property set to sensitive                                                   //@F8A
                    //@K1D         request.setScrollableCursorFlag (DBSQLRequestDS.CURSOR_NOT_SCROLLABLE_SENSITIVE);    //@F8A
                    //@K1D }
                    //@K1D //@F8 Else, resultSetType_ is ResultSet.TYPE_CURSOR_SENSITIVE
                    //@K1D else
                    //@K1D {
                    //@K1D     if(cursorSensitivity.equalsIgnoreCase(JDProperties.CURSOR_SENSITIVITY_INSENSITIVE))      //@F8A
                    //@K1D         request.setScrollableCursorFlag (DBSQLRequestDS.CURSOR_SCROLLABLE_INSENSITIVE);       //@F8A
                    //@K1D     else  //else property set to sensitive                                                    //@F8A
                    //@K1D         request.setScrollableCursorFlag(DBSQLRequestDS.CURSOR_SCROLLABLE_SENSITIVE);          //@F8A
                    //@K1D }
                    //If earlier than V5R2, do what we always have                                                       //@K1A
                    if(connection_.getVRM() < JDUtilities.vrm520)    //@K1A
                    {
                        //@K1A
                        if(resultSetType_ == ResultSet.TYPE_FORWARD_ONLY)    //@K1A
                            request.setScrollableCursorFlag (DBSQLRequestDS.CURSOR_NOT_SCROLLABLE_ASENSITIVE);    //@K1A        Option 0
                        else    //@K1A
                            request.setScrollableCursorFlag (DBSQLRequestDS.CURSOR_SCROLLABLE_ASENSITIVE);    //@K1A        Option 1
                    }    //@K1A
                    else if ((autoGeneratedKeys_ == RETURN_GENERATED_KEYS) && connection_.getVRM() >= JDUtilities.vrm610)   //@GKA
                    {                                                                                                       //@GKA
                        // The user requested generated keys so we wrapped the INSERT with a SELECT                         //@GKA
                        // The result set returned will be for the generated keys and should be read-only, insensitive      //@GKA
                        // We want to ignore any property or settings specified on the STATEMENT or CONNECTION              //@GKA
                        request.setScrollableCursorFlag(DBSQLRequestDS.CURSOR_SCROLLABLE_INSENSITIVE);                      //@GKA
                    }                                                                                                       //@GKA
                    else {
                      /* @H1A Use common routine to determine scrollability */ 	
                      request.setScrollableCursorFlag(
                          AS400JDBCResultSet.getDBSQLRequestDSCursorType(cursorSensitivity, resultSetType_, resultSetConcurrency_));
                    }

                    // Check system level before sending new code point
                    if(connection_.getVRM() >= JDUtilities.vrm520)    // @G4A
                    {
                        // @G4A
                        if(resultSetHoldability_ == AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT)    // @G4A
                            request.setResultSetHoldabilityOption((byte)0xD5);    // @G4A
                        else if(resultSetHoldability_ == AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT)    // @G4A
                            request.setResultSetHoldabilityOption((byte)0xE8);    // @G4A
                        // else resultSetHoldability_ == AS400JDBCResultSet.HOLDABILITY_NOT_SPECIFIED, // @G4A
                        // so don't send the code point because the user did not tell us to            // @G4A
                    }

                    disableRllCompression_ = false; //@L9A
                    commonExecuteBefore(sqlStatement, request);

                    if (commonExecuteReply != null) { commonExecuteReply.returnToPool(); commonExecuteReply=null; }

                    if (disableRllCompression_) {  //@L9A
                      connection_.setDisableCompression(true); 
                    }
                    commonExecuteReply = connection_.sendAndReceive(request, id_);    //@P0C
                    if (disableRllCompression_) {  //@L9A
                      connection_.setDisableCompression(false); 
                    }

                    // Gather information from the reply.
                    cursor_.processConcurrencyOverride(openAttributes, commonExecuteReply);    // @E1A @EAC

                    cursor_.processCursorAttributes(commonExecuteReply);                  //@cur

                    transactionManager_.processCommitOnReturn(commonExecuteReply);    // @E2A
                    DBReplySQLCA sqlca = commonExecuteReply.getSQLCA();
                    DBData resultData = null;
                    if(fetchFirstBlock) resultData = commonExecuteReply.getResultData();

                    // Note the number of rows inserted/updated
                    rowsInserted_ = sqlca.getErrd(3);    // @G5A

                    // Check for system errors.  Take note on prefetch
                    // if the last block was fetched.
                    int errorClass = commonExecuteReply.getErrorClass();
                    int returnCode = commonExecuteReply.getReturnCode();

                    //
                    // Check for the case where the errorClass is zero, but there
                    // is truncation.  If so, set the errorClass to 2 so the  
                    // warning is processed. 
                    // @S2A
                      
                    if (errorClass == 0) {
                      String sqlState = sqlca.getSQLState (connection_.converter_); 
                      if ("01004".equals(sqlState)) { 
                         errorClass = 2; 
                      }
                    }
                    
                    // NOTE:  We currently do not have a good way to handle the case
                    //        when the exit program rejects the request.  
                    // 
                    // Error Class:                   0x0008 - Exit Program Error
                    // Error Return Code:             -101 - User exit program rejected request.
                    //
                    // Currently, an empty SQLCA is returned which causes an 
                    // array index out of bounds exception when calling 
                    // DBReplySQLCA.getErrp below.
                    // 
                    
                    
                    // Remember that a cursor is open even when most
                    // errors occur.
                    //
                    // Except:
                    //   * When a query timeout occurs, the system
                    //     does not leave a cursor open.
                    if(openNeeded && (errorClass != 1 || returnCode != -666)) cursor_.setState(false);

                    // Take note on prefetch if the last block was fetched.
                    boolean lastBlock = false;
                    boolean bypassExceptionWarning = false;  //@pda (issue 32120) in special errorClass/returnCode cases below, we use sqlca to see if there is a real error
                    if((((errorClass == 1) && (returnCode == 100))
                       || ((errorClass == 2) && (returnCode == 701)))
                       && functionId == DBSQLRequestDS.FUNCTIONID_OPEN_DESCRIBE_FETCH)      // make sure we attempted to prefetch data, otherwise post a warning
                    {
                        lastBlock = true;
                        returnCode = sqlca.getSQLCode();    //@pda (issue 32120) get rc from SQLCA
                        String sqlState = sqlca.getSQLState (connection_.converter_);              //@issue 34500
                        if(sqlState.startsWith("00") || sqlState.startsWith("02"))                    //@pda (issue 32120)  //@issue 34500 //@35199
                        	bypassExceptionWarning = true;  //@pda (issue 32120)
                    }
                    else if((errorClass == 2) && (returnCode == 700)
                            && (functionId == DBSQLRequestDS.FUNCTIONID_OPEN_DESCRIBE_FETCH)) //@pda perf2 - fetch/close
                    {
                        lastBlock = true;
                        cursor_.setState(true); //closed cursor already on system
                        returnCode = sqlca.getSQLCode();    //@pda (issue 32120) get rc from SQLCA
                        String sqlState = sqlca.getSQLState (connection_.converter_);              //@issue 34500
                        if(sqlState.startsWith("00") || sqlState.startsWith("02"))                 //@pda (issue 32120)  //@issue 34500 //@35199
                        	bypassExceptionWarning = true;  //@pda (issue 32120)
                    }


                    //else //@PDD  check for errors even on cases above (issue 32120)
                    if(errorClass != 0 && bypassExceptionWarning == false)  //@pdc (issue 32120)
                    {
                        positionOfSyntaxError_ = sqlca.getErrd(5);    //@F10A

                        if(returnCode < 0)
                        {
                          //
                          // Check if error came from a combined opened fetch...
                          // If so, delay error until fetch occurs.  @F3A
                          //
                          int errd6 = sqlca.getErrd(6);
                          String errp = sqlca.getErrp(connection_.converter_);   /*@N7A*/ 
                          if ( errd6 == 1 || 
                              ( "QSQFETCH".equals(errp) && 
                                  (functionId == DBSQLRequestDS.FUNCTIONID_OPEN_DESCRIBE_FETCH))) {
                             // Delay error
                             // Only delay error from fetch if coming from open_describe_fetch @O8A
                              try {
                                JDError.throwSQLException(this, connection_, id_, errorClass, returnCode);
                              } catch (SQLException e) {
                                savedException = e;
                              }
                          } else {

                            JDError.throwSQLException(this, connection_, id_, errorClass, returnCode);
                          }
                        }
                        else
                        {
                            String sqlState = sqlca.getSQLState (connection_.converter_);  //@igwrn
                            postWarning(JDError.getSQLWarning(connection_, id_, errorClass, returnCode));
                        }
                    }

                    //Make an auto-generated key result set if it was requested          //@G4A
                    if(autoGeneratedKeys_ == RETURN_GENERATED_KEYS &&    //@G4A
                       (connection_.getVRM() >= JDUtilities.vrm520) &&
                       ((connection_.getVRM() < JDUtilities.vrm610) || !sqlStatement.isSelectFromInsert()))    //@F5A @F6C @GKC
                        //@F6D&& generatedKeys_ == null)                                 //@F5A
                        makeGeneratedKeyResultSet(returnCode, sqlca);    //@G4A
                    else if(generatedKeys_ != null)    //@PDA genkeys - handle previous genkeys
                    {
                        generatedKeys_.close();
                        generatedKeys_ = null;
                    }


                    //@F5D This belongs on the prepare, not the execute
                    //@F5D @F3A If user asked us to parse out extended metadata, then make an object
                    //@K3A Added back.. This belongs here for stored procedure result sets
                    // extendedMetaData is set to true only for all statements. 
                    if (extendedMetaData)                                                         //@F3A
                     {                                                                             //@F3A
                         extendedColumnDescriptors_ = commonExecuteReply.getExtendedColumnDescriptors();        //@F3A
                     }                                                                             //@F3A

                    // Compute the update count and result set .
                    if(openNeeded)
                    {
                      // @B5A Check for null pointer.
                    	int rowLength = 0;
                    	if (resultRow != null) {
                    		rowLength = resultRow.getRowLength();
                    	}
                        JDServerRowCache rowCache;
                        if((fetchFirstBlock) && (resultData != null))
                            rowCache = new JDServerRowCache (resultRow,
                                                             connection_, id_,
                                                             getBlockingFactor (cursorSensitivity, sqlStatement, rowLength), resultData,
                                                             lastBlock, resultSetType_, cursor_); //@pdc perf2 - fetch/close
                        else
                            rowCache = new JDServerRowCache (resultRow,
                                                             connection_, id_,
                                                             getBlockingFactor (cursorSensitivity, sqlStatement, rowLength), lastBlock, resultSetType_, cursor_); //@PDC perf //@pdc perf2 - fetch/close

                        // If the result set concurrency is updatable, check to                            @E1C
                        // see if the system overrode the cursor type to read only.                        @E1C
                        // Don't worry about the other direction (system overrides                         @E1C
                        // from read-only to updatable).                                                   @E1C
                        int actualConcurrency = (resultSetConcurrency_ == ResultSet.CONCUR_UPDATABLE)    // @E1C
                                                ? cursor_.getConcurrency() : resultSetConcurrency_;    // @E1C @EAC

                        if((autoGeneratedKeys_ == RETURN_GENERATED_KEYS) &&
                            (connection_.getVRM() >= JDUtilities.vrm610) &&
                            sqlStatement.isSelectFromInsert())    //@GKA
                        {
                            // this will be the generated keys result set                                                                                                   //@GKA
                            updateCount_ = sqlca.getErrd(3);                                                                //@GKA
                            rowCountEstimate_ = -1;                                                                         //@GKA
                            generatedKeys_ = new AS400JDBCResultSet (this, sqlStatement, rowCache, connection_.getCatalog(),//@GKA
                                                                     cursor_.getName(), maxRows_,                           //@GKA
                                                                     ResultSet.TYPE_SCROLL_INSENSITIVE,                     //@GKA
                                                                     ResultSet.CONCUR_READ_ONLY, fetchDirection_, fetchSize_,
                                                                     extendedColumnDescriptors_); //@GKA@P6A
                        }                                                                                                   //@GKA
                        else                                                                                                //@GKA
                        {                                                                                                   //@GKA
                            updateCount_ = -1;    // @ECM
                            rowCountEstimate_ = sqlca.getErrd (3);    //@F1C                                          // @ECA
                            resultSet_ = new AS400JDBCResultSet (this,
                                                             sqlStatement, rowCache, connection_.getCatalog(),
                                                             cursor_.getName(), maxRows_, resultSetType_,
                                                             actualConcurrency, fetchDirection_, fetchSize_,
                                                             extendedColumnDescriptors_);    // @E1C@P6A
                            if(resultSet_.getConcurrency () != resultSetConcurrency_ && resultSetConcurrency_ == ResultSet.CONCUR_UPDATABLE) //@nowarn only warn if concurrency level is lessened
                                postWarning (JDError.getSQLWarning (JDError.WARN_OPTION_VALUE_CHANGED));
                            if (savedException != null) {     /*@F3A*/
                              resultSet_.addSavedException(savedException);
                            }
                        }                                                                                                   //@GKA
                    }
                    else
                    {
                        updateCount_ = sqlca.getErrd (3);    //@F1C
                        rowCountEstimate_ = -1;    // @ECC
                    }

                    // Compute the number of results.
                    //@541D boolean isCall = (sqlStatement.getNativeType () == JDSQLStatement.TYPE_CALL);
                    if(isCall)
                        numberOfResults_ = sqlca.getErrd (2);    //@F1C
                    else
                        numberOfResults_ = 0;

                    if(extendedMetaData)    //@541A
                    {
                        extendedColumnDescriptors_ = commonExecuteReply.getExtendedColumnDescriptors ();    //@F5A
                        cursor_.setExtendedMetaData(extendedMetaData);                                      //@K3A
                    }

                    // If this is a CALL and result sets came back, but
                    // no format was returned, then open the cursor. The
                    // result set must be forward only and read only.
                    // This is a system restriction.                                    @EBA
                    // As of V5R3 the restriction to be forward only no longer applies  @KBA
                    if(sqlStatement != null)
                    {
                        if((isCall) && (numberOfResults_ > 0) && (resultSet_ == null))
                        {
                            boolean preV5R3 = connection_.getVRM() < JDUtilities.vrm530;

                            // Change the result set type based on the current attributes
                            // unless forward only cursors were requested.   This must
                            // be kept in sync with similar code ins AS400JDBCResultSet
                            // @C4A
                            int callResultSetType ;
                            if (resultSetType_ == ResultSet.TYPE_FORWARD_ONLY) {
                            	// The user requested FORWARD_ONLY, so the result set will
                            	// only be usable as forward only.
                            	callResultSetType = ResultSet.TYPE_FORWARD_ONLY;
                            } else if(cursor_.getCursorAttributeScrollable() == 0)
                                callResultSetType =  ResultSet.TYPE_FORWARD_ONLY;
                            else if(cursor_.getCursorAttributeSensitive() == 0)
                                callResultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
                            else if(cursor_.getCursorAttributeSensitive() == 1)
                                callResultSetType = ResultSet.TYPE_SCROLL_SENSITIVE;
                            else
                            	callResultSetType = resultSetType_;

                            JDServerRow row = new JDServerRow (
                                                              connection_, id_, cursor_.openDescribe (openAttributes,
                                                                                                      callResultSetType), settings_);          //@KBA
                            JDServerRowCache rowCache = new JDServerRowCache (row,
                                                                              connection_, id_, 
                                                                              getBlockingFactor (cursorSensitivity, sqlStatement,
                                                                                                                   row.getRowLength()), false, (preV5R3 ? ResultSet.TYPE_FORWARD_ONLY : resultSetType_), cursor_);  //@PDC perf //@pda perf2 - fetch/close

                            
                            /*@K3A@P6M*/
                            if (extendedMetaData) {
                                DBExtendedColumnDescriptors newExtendedColumnDescriptors = cursor_.getExtendedColumnDescriptors();
                                if (newExtendedColumnDescriptors != null) {
                                  extendedColumnDescriptors_ = newExtendedColumnDescriptors; 
                                }
                            }
                            
                            
                            //if pre-v5r3 create a FORWARD_ONLY RESULT SET
                            
                            if(preV5R3)                                                           //@KBA
                            {
                                resultSet_ = new AS400JDBCResultSet (this,
                                                                     sqlStatement, rowCache, connection_.getCatalog(),
                                                                     cursor_.getName(), maxRows_, ResultSet.TYPE_FORWARD_ONLY,
                                                                     ResultSet.CONCUR_READ_ONLY, fetchDirection_,
                                                                     fetchSize_, extendedColumnDescriptors_);  //@P6C
                            }
                            else                                                                                                    //@KBA
                            {                                                                                                       //@KBA
                                resultSet_ = new AS400JDBCResultSet (this,                                                          //@KBA
                                                                 sqlStatement, rowCache, connection_.getCatalog(),                  //@KBA
                                                                 cursor_.getName(), maxRows_, callResultSetType,                       //@KBA
                                                                 ResultSet.CONCUR_READ_ONLY, fetchDirection_,                       //@KBA
                                                                 fetchSize_, extendedColumnDescriptors_);     //@P6C                                                   //@KBA
                            }

                            if(resultSet_.getConcurrency () != resultSetConcurrency_)
                                postWarning (JDError.getSQLWarning (JDError.WARN_OPTION_VALUE_CHANGED));
                            
                        }
                    }

                    commonExecuteAfter (sqlStatement, commonExecuteReply);
                } catch (SQLException sqlex) {

                	// Handle interrupted exception
                	String messageText = sqlex.getMessage();
                	messageText = messageText.toLowerCase();
                	if (messageText.indexOf("internal driver error") >= 0) {
						if (messageText.indexOf("interrupted") > 0) {
							threadInterrupted = true;
						}
					}
                	throw sqlex;
                }
                finally
                {    //@P0A
                    if(request != null)  {
                    	request.returnToPool();    request = null;
                    }
                    // This can be returned.  See comment at declaration.  @B5A
                    if (commonExecuteReply != null)  { commonExecuteReply.returnToPool(); commonExecuteReply = null; }

                }
            }
            catch(DBDataStreamException e)
            {
                JDError.throwSQLException (JDError.EXC_INTERNAL, e);
            }

            // Inform the transaction manager that a statement
            // was executed.
            transactionManager_.statementExecuted ();

            // Check to see if the statement was cancelled.
            if(cancelled_)
                JDError.throwSQLException (JDError.EXC_OPERATION_CANCELLED);

            // Output a summary as a trace message.   The * signifies that the
            // statement name comes from the RPB.
            if(JDTrace.isTraceOn())
            {
                JDTrace.logInformation (this,
                                        "Executed " + (usedNameOverride ? (nameOverride_) : (name_ + "*"))
                                        + ", SQL Statement --> [" + sqlStatement + "]");
                JDTrace.logInformation (this,
                                        "Update count = " + updateCount_);
                JDTrace.logInformation (this,
                                        "Result set = " + (resultSet_ != null));
                JDTrace.logInformation (this,
                                        "Number of result sets = " + numberOfResults_);
                JDTrace.logInformation (this,    // @ECA
                                        "Row count estimate = " + rowCountEstimate_);    // @ECA
            }
        }
      } finally {
         /*@D4A*/
        if (connection_.isQueryTimeoutMechanismCancel()) {
          endCancelThread();
        }

      }
    }


    /**
    Performs common operations needed after an execute.

    @param  sqlStatement    The SQL statement.
    @param  reply           The execute reply.

    @exception      SQLException    If an error occurs.
    **/
    //
    // This method is intended to be overriden, if needed.
    //
    void commonExecuteAfter (JDSQLStatement sqlStatement,
                             DBReplyRequestedDS reply)    // private protected
    throws SQLException
    {
      currentJDSQLStatement_ = null; 

        // Nothing.
    }



    /**
    Performs common operations needed before an execute.

    @param  sqlStatement    The SQL statement.
    @param  request         The execute request.

    @exception      SQLException    If an error occurs.
    **/
    //
    // This method is intended to be overriden, if needed.
    //
    void commonExecuteBefore (JDSQLStatement sqlStatement,
                              DBSQLRequestDS request)    // private protected
    throws SQLException
    {
        // Clear warnings.
        currentJDSQLStatement_ = sqlStatement; 
        clearWarnings ();
    }



    /**
    Prepares (pre-compiles) the SQL statement on the IBM i system.

    @param   sqlStatement   The SQL statement.
    @return                 The result row or null if none.

    @exception  SQLException    If an error occurs.
    **/
    //
    // Do not override this method.  Instead, override
    // commonPrepareBefore(), commonPrepareAfter() and
    // commonPrepareBypass() as needed.
    //
    JDServerRow commonPrepare (JDSQLStatement sqlStatement)    // private protected
    throws SQLException
    {
      JDServerRow resultRow = null;
      currentJDSQLStatement_ = sqlStatement; 

      try {
        /*@D4A*/
        if (connection_.isQueryTimeoutMechanismCancel()) {
          startCancelThread();
        }

        cancelled_ = false;

        connection_.checkAccess (sqlStatement);
        nameOverride_ = "";

        // Check for DRDA connect or disconnect.  @B1A
        if(sqlStatement.isDRDAConnect ())
        {
            connection_.setDRDA (true);

            // It is possible at this point, that current statement
            // id does not follow the DRDA rules (defined in
            // AS400JDBCConnection.getUnusedId()), but we will not
            // work around that at this point.  This simple workaround
            // is to create a new Statement immediately after connecting.
        }
        else if(sqlStatement.isDRDADisconnect ())
            connection_.setDRDA (false);

        // Create the SQL package if necessary.
        if(! packageManager_.isCreated())
            packageManager_.create ();

        // There are three distinct cases. Exactly one of these
        // branches will be taken.

        // CASE 1: The statement appears in the cached package.
        //
        // If the SQL package is cached, check to see if this SQL
        // statement is in there.  If so, then gather data accordingly
        // and skip the prepare.
        //
        if(packageManager_.isCached())
        {
            if(sqlStatement.isPackaged())
            {
                int i = packageManager_.getCachedStatementIndex (sqlStatement);
                if(i != -1)
                {

                    // Gather results from package cache.
                    DBDataFormat dataFormat = packageManager_.getCachedDataFormat (i);
                    if(dataFormat == null)
                        resultRow = null;
                    else
                        resultRow = new JDServerRow (connection_, id_,
                                                     dataFormat, settings_);
                    //@re-prep check if one of the columns is a lob or locator on resultset columns
                    if( resultRow != null && resultRow.containsLob_) //@re-prep
                    {
                        resultRow = null;                           //@re-prep output lobs
                    }                                               //@re-prep

                    commonPrepareBypass (sqlStatement, i);

                    //@re-prep input lobs on prepared statement
                    if(parameterRow_ != null && parameterRow_.containsLob_)  //@re-prep
                    {                                                        //@re-prep
                        nameOverride_ = "";                                  //@re-prep
                        // Output a summary as a trace message.
                        if(JDTrace.isTraceOn())                              //@re-prep
                            JDTrace.logInformation (this,
                                                "Statement [" + sqlStatement + "] was found "
                                                + "in the cached package "
                                                + " but must be re-prepared since it contains a lob or locator");  //@re-prep
                    }                                                                                              //@re-prep
                    else                                                                                           //@re-prep
                    {                                                                                              //@re-prep
                        nameOverride_ = packageManager_.getCachedStatementName (i);
                        // Output a summary as a trace message.
                        if(JDTrace.isTraceOn())
                            JDTrace.logInformation (this,
                                                "Statement [" + sqlStatement + "] was found "
                                                + "in the cached package as " + nameOverride_);
                    }                                           //@re-prep
                }
            }
        }

        // If the SQL package is not cached, then we must prepare
        // the statement on the system.
        if(nameOverride_.length() == 0)
        {

            // @E7A - start
            //
            // CASE 2a: Statement is a DRDA CONNECT.
            //
            if(sqlStatement.getNativeType() == JDSQLStatement.TYPE_CONNECT)
            {

                // Sync up the RPB.
                syncRPB();

                DBSQLRequestDS request = null;    //@P0A
                try
                {

                    request = DBDSPool.getDBSQLRequestDS(DBSQLRequestDS.FUNCTIONID_CONNECT, id_, DBSQLRequestDS.ORS_BITMAP_RETURN_DATA+DBSQLRequestDS.ORS_BITMAP_SQLCA, 0);    //@P0C

                    boolean extended = false;                                                         //@540
                    if(connection_.getVRM() >= JDUtilities.vrm540) extended = true;                   //@540
                    //Bidi-HCG request.setStatementText(sqlStatement.toString(), connection_.unicodeConverter_, extended); //@E3C @P0C @540C
                    request.setStatementText(sqlStatement.toString(), connection_.packageCCSID_Converter, extended);//Bidi-HCG
                    request.setStatementType (sqlStatement.getNativeType ());

                    if(packageManager_.isEnabled())
                    {
                        if(sqlStatement.isPackaged())
                        {
                            request.setPrepareOption (1);
                            request.setPackageName (packageManager_.getName (), connection_.converter_);    //@P0C
                        }
                        else
                        {
                            request.setPrepareOption (0);
                            request.setPackageName(null, connection_.converter_);         //send empty code point per
                        }
                    }
                    else
                        request.setPrepareOption (0);

                    commonPrepareBefore (sqlStatement, request);
                    commonExecuteBefore (sqlStatement, request);

                    if (execImmediateReply != null) {
                    	execImmediateReply.returnToPool(); execImmediateReply = null;
                    }
                    if (normalPrepareReply != null) {
                    	normalPrepareReply.returnToPool();normalPrepareReply = null;
                    }
                    if (connectReply != null) { connectReply.returnToPool(); connectReply=null; }
                    connectReply = connection_.sendAndReceive (request, id_);    //@P0C

                    int errorClass = connectReply.getErrorClass();
                    int returnCode = connectReply.getReturnCode();

                    if(errorClass != 0)
                    {
                        positionOfSyntaxError_ = connectReply.getSQLCA().getErrd(5);    //@F10A

                        if(returnCode < 0)
                            JDError.throwSQLException (this, connection_, id_, errorClass, returnCode);
                        else
                            postWarning (JDError.getSQLWarning (connection_, id_, errorClass, returnCode));
                    }

                    // Compute the update count and number of results.
                    updateCount_ = 0;
                    numberOfResults_ = 0;

                    commonPrepareAfter (sqlStatement, connectReply);
                    commonExecuteAfter (sqlStatement, connectReply);

                }
                catch(DBDataStreamException e)
                {
                    JDError.throwSQLException (JDError.EXC_INTERNAL, e);
                }
                finally
                {    //@P0A
                    if(request != null)  {
                    		request.returnToPool();    request=null;
                    }
                    if (connectReply != null)        { connectReply.returnToPool(); connectReply = null; }      /*@B5A*/

                }

                // Inform the transaction manager that a statement
                // was executed.
                transactionManager_.statementExecuted ();

                // Output a summary as a trace message.   The * signifies that the
                // statement name comes from the RPB.
                if(JDTrace.isTraceOn())
                {
                    JDTrace.logInformation (this,
                                            "Executed connect " + name_ + "*, SQL Statement -->[" + sqlStatement + "]");
                }

            }
            // @E7A - end

            // CASE 2: Statement can be executed immediately.
            //
            // This is essentially the prepare and execute combined
            // in one datastream.  We will then be able to skip the
            // execute step later.
            //
            else if((allowImmediate_) && (sqlStatement.isImmediatelyExecutable ()))
            {    // @E7C

                // Sync up the RPB.
                syncRPB ();

                DBSQLRequestDS request = null;    //@P0A
                try
                {

                    int requestedORS = DBSQLRequestDS.ORS_BITMAP_RETURN_DATA+DBSQLRequestDS.ORS_BITMAP_SQLCA;    //@F5A
                    boolean isCall = (sqlStatement.getNativeType () == JDSQLStatement.TYPE_CALL);      //@cur
                    if(connection_.getVRM() >= JDUtilities.vrm610 )                           //@cur //@isol
                        requestedORS += DBSQLRequestDS.ORS_BITMAP_CURSOR_ATTRIBUTES;                   //@cur
                    //@F5A If we are on a system that supports extended column descriptors and if the              //@F5A
                    //@F5A user asked for them, send the extended column descriptors code point.                   //@F5A
                    boolean extendedMetaData = false;    //@F5A
                    if(connection_.getVRM() >= JDUtilities.vrm520)    //@F5A
                    {
                        //@F5A
                        extendedMetaData = connection_.getProperties().getBoolean(JDProperties.EXTENDED_METADATA);    //@F5A
                        if(extendedMetaData)    //@F5A
                        {
                            //@F5A
                            requestedORS = requestedORS + DBSQLRequestDS.ORS_BITMAP_EXTENDED_COLUMN_DESCRIPTORS;    //@F5A
                        }    //@F5A
                    }

                    request = DBDSPool.getDBSQLRequestDS (DBSQLRequestDS.FUNCTIONID_EXECUTE_IMMEDIATE, id_, requestedORS, 0);    //@P0C @F5C

                    boolean extended = false;                                                         //@540
                    if(connection_.getVRM() >= JDUtilities.vrm540) extended = true;                   //@540
                    //Bidi-HCG request.setStatementText(sqlStatement.toString(), connection_.unicodeConverter_, extended); //@E3C @P0C @540C
                    request.setStatementText(sqlStatement.toString(), connection_.packageCCSID_Converter, extended);//Bidi-HCG
                    request.setStatementType (sqlStatement.getNativeType ());

                    int openAttributes = cursor_.getOpenAttributes (sqlStatement, blockCriteria_);
                    request.setOpenAttributes (openAttributes);

                    if(packageManager_.isEnabled())
                    {
                        if(sqlStatement.isPackaged())
                        {
                            request.setPrepareOption (1);
                            request.setPackageName (packageManager_.getName (), connection_.converter_);    //@P0C
                        }
                        else
                        {
                            request.setPrepareOption (0);
                            request.setPackageName(null, connection_.converter_);          //send empty code point
                        }
                    }
                    else
                        request.setPrepareOption (0);

                    //@F5A Don't have to check if a v5r2 system, because extendedMetaData
                    //@F5A can only be true if we are running to v5r2
                    if(extendedMetaData)    //@F5A
                    {
                        //@F5A
                        request.setExtendedColumnDescriptorOption((byte)0xF1);    //@F5A
                    }    //@F5A

                    commonPrepareBefore (sqlStatement, request);
                    commonExecuteBefore (sqlStatement, request);

                    if (connectReply != null) {
                    	connectReply.returnToPool();connectReply = null;
                    }
                    if (normalPrepareReply != null) {
                    	normalPrepareReply.returnToPool();normalPrepareReply = null;
                    }

                    if (execImmediateReply != null) { execImmediateReply.returnToPool(); execImmediateReply=null;}
                    execImmediateReply = connection_.sendAndReceive (request, id_);    //@P0C

                    int errorClass = execImmediateReply.getErrorClass();
                    int returnCode = execImmediateReply.getReturnCode();

                    DBReplySQLCA sqlca = execImmediateReply.getSQLCA ();    //@F10M

                    if(errorClass != 0)
                    {
                        positionOfSyntaxError_ = sqlca.getErrd(5);    //@F10A
                        if(returnCode < 0)
                            JDError.throwSQLException (this, connection_, id_, errorClass, returnCode);
                        else
                            postWarning (JDError.getSQLWarning (connection_, id_, errorClass, returnCode));
                    }

                    transactionManager_.processCommitOnReturn(execImmediateReply);    // @E2A

                    cursor_.processCursorAttributes(execImmediateReply);                   //@cur

                    // Compute the update count.
                    //@F10M DBReplySQLCA sqlca = reply.getSQLCA ();
                    updateCount_ = sqlca.getErrd (3);    //@F1C
                    rowCountEstimate_ = -1;    // @ECA

                    //@F5A Don't have to check if a v5r2 system, because extendedMetaData
                    //@F5A can only be true if we are running to v5r2
                    if(extendedMetaData)    //@F5A
                    {
                        //@F5A
                        extendedColumnDescriptors_ = execImmediateReply.getExtendedColumnDescriptors ();    //@F5A
                    }    //@F5A

                    //Make an auto-generated key result set if it was requested              //@F5A
                    // Note:  This should not happen if running to a release after V5R4 as the insert will always be wrapped with a SELECT //@GKA
                    if(autoGeneratedKeys_ == RETURN_GENERATED_KEYS &&    //@F5A
                       (connection_.getVRM() >= JDUtilities.vrm520))    //@F5A
                        makeGeneratedKeyResultSet(returnCode, sqlca);    //@F5A
                    else if(generatedKeys_ != null)    //@PDA genkeys - handle previous genkeys
                    {
                        generatedKeys_.close();
                        generatedKeys_ = null;
                    }

                    // Compute the number of results.
                    //boolean isCall = (sqlStatement.getNativeType () == JDSQLStatement.TYPE_CALL); //@cur moved above
                    if(    /*(numberOfResults_ == 0) && */(isCall))
                        numberOfResults_ = sqlca.getErrd (2);    //@F1C
                    else
                        numberOfResults_ = 0;

                    // If this is a CALL and result sets came back, but
                    // no format was returned, then open the cursor.  The result
                    // set must be forward only and read only.
                    // This is a system restriction.                                    @EBA
                    // As of V5R3, the restriction to be forward only no longer applies @KBA
                    if((isCall == true) && (numberOfResults_ > 0))
                    {
                        boolean preV5R3 = connection_.getVRM() < JDUtilities.vrm530;
                        if (extendedMetaData) {  /*@K3A*/
                          cursor_.setExtendedMetaData(extendedMetaData); 
                        }
                        JDServerRow row = new JDServerRow (connection_, id_,
                                                           cursor_.openDescribe (openAttributes, resultSetType_),             //@KBA
                                                           settings_);
                        String cursorSensitivity = connection_.getProperties().getString(JDProperties.CURSOR_SENSITIVITY);    //@F8A
                        
                        JDServerRowCache rowCache = new JDServerRowCache (row, connection_, id_, getBlockingFactor (cursorSensitivity, 
                                                                                                                   sqlStatement, row.getRowLength()), false, (preV5R3 ? ResultSet.TYPE_FORWARD_ONLY : resultSetType_), cursor_); //@PDC perf //@pda perf2 - fetch/close

                        //if pre-v5r3 create a FORWARD_ONLY RESULT SET
                            if(preV5R3)                                                           //@KBA
                            {
                                resultSet_ = new AS400JDBCResultSet (this,
                                                                     sqlStatement, rowCache, connection_.getCatalog(),
                                                                     cursor_.getName(), maxRows_, ResultSet.TYPE_FORWARD_ONLY,
                                                                     ResultSet.CONCUR_READ_ONLY, fetchDirection_,
                                                                     fetchSize_, extendedColumnDescriptors_);                        //@P6C
                            }
                            else                                                                                                    //@KBA
                            {                                                                                                       //@KBA
                                resultSet_ = new AS400JDBCResultSet (this,                                                          //@KBA
                                                                 sqlStatement, rowCache, connection_.getCatalog(),                  //@KBA
                                                                 cursor_.getName(), maxRows_, resultSetType_,                       //@KBA
                                                                 ResultSet.CONCUR_READ_ONLY, fetchDirection_,                       //@KBA
                                                                 fetchSize_, extendedColumnDescriptors_);                           //@KBA@P6C
                            }

                        if(resultSet_.getConcurrency () != resultSetConcurrency_)
                            postWarning (JDError.getSQLWarning (JDError.WARN_OPTION_VALUE_CHANGED));
                    }

                    commonPrepareAfter (sqlStatement, execImmediateReply);
                    commonExecuteAfter (sqlStatement, execImmediateReply);
                }
                catch(DBDataStreamException e)
                {
                    JDError.throwSQLException (JDError.EXC_INTERNAL, e);
                }
                finally
                {    //@P0A
                    if(request != null) {
                    		request.returnToPool();  request = null;
                    }
                    if (execImmediateReply != null)  { execImmediateReply.returnToPool(); execImmediateReply = null; }   /*@B5A*/
                }

                // Inform the transaction manager that a statement
                // was executed.
                transactionManager_.statementExecuted ();

                // Output a summary as a trace message.   The * signifies that the
                // statement name comes from the RPB.
                if(JDTrace.isTraceOn())
                {
                    JDTrace.logInformation (this,
                                            "Executed immediately " + name_ + "*, SQL Statement -->[" + sqlStatement + "]");
                    JDTrace.logInformation (this,
                                            "Update count = " + updateCount_);
                    JDTrace.logInformation (this,
                                            "Result set = " + (resultSet_ != null));
                    JDTrace.logInformation (this,
                                            "Number of result sets = " + numberOfResults_);
                }

            }

            // CASE 3: Normal prepare.
            //
            else
            {

                // Sync up the RPB.
                syncRPB ();

                DBSQLRequestDS request = null;    //@P0A
                if (normalPrepareReply != null) {
                	normalPrepareReply.returnToPool(); normalPrepareReply = null;
                } /* B5A */
                try
                {
                    int requestedORS = DBSQLRequestDS.ORS_BITMAP_RETURN_DATA+DBSQLRequestDS.ORS_BITMAP_DATA_FORMAT+DBSQLRequestDS.ORS_BITMAP_SQLCA;    //@F5A @F10C
                    //@F5A If we are on a system that supports extended column descriptors and if the               //@F5A
                    //@F5A user asked for them, send the extended column descriptors code point.                   //@F5A
                    boolean extendedMetaData = false;    //@F5A
                    if(connection_.getVRM() >= JDUtilities.vrm520)    //@F5A
                    {
                        //@F5A
                        extendedMetaData = connection_.getProperties().getBoolean(JDProperties.EXTENDED_METADATA);    //@F5A
                        if(extendedMetaData)    //@F5A
                        {
                            //@F5A
                            requestedORS = requestedORS + DBSQLRequestDS.ORS_BITMAP_EXTENDED_COLUMN_DESCRIPTORS;    //@F5A
                        }    //@F5A
                    }

                    request = DBDSPool.getDBSQLRequestDS(DBSQLRequestDS.FUNCTIONID_PREPARE_DESCRIBE, id_, requestedORS, 0);    //@P0C @F5C

                    boolean extended = false;                                                         //@540
                    if(connection_.getVRM() >= JDUtilities.vrm540) extended = true;                   //@540
                    //Bidi-HCG request.setStatementText(sqlStatement.toString(), connection_.unicodeConverter_, extended); //@E3C @P0C @540C
                    request.setStatementText(sqlStatement.toString(), connection_.packageCCSID_Converter, extended);//Bidi-HCG
                    request.setStatementType (sqlStatement.getNativeType ());

                    if(packageManager_.isEnabled())
                    {
                        if(sqlStatement.isPackaged())
                        {
                            request.setPrepareOption (1);
                            request.setPackageName (packageManager_.getName (), connection_.converter_);    //@P0C
                        }
                        else
                        {
                            request.setPrepareOption (0);
                            request.setPackageName(null, connection_.converter_);              //send empty code point
                        }
                    }
                    else
                        request.setPrepareOption (0);

                    //@F5A Don't have to check if a v5r2 system, because extendedMetaData
                    //@F5A can only be true if we are running to v5r2
                    if(extendedMetaData)    //@F5A
                    {
                        //@F5A
                        request.setExtendedColumnDescriptorOption((byte)0xF1);    //@F5A
                    }    //@F5A

                    commonPrepareBefore (sqlStatement, request);
                    if (execImmediateReply != null) {
                    	execImmediateReply.returnToPool();execImmediateReply = null;
                    }
                    if (connectReply != null) {
                    	connectReply.returnToPool();connectReply = null;
                    }

                    if (normalPrepareReply != null) { normalPrepareReply.returnToPool(); normalPrepareReply=null; }
                    normalPrepareReply = connection_.sendAndReceive (request, id_);    //@P0C

                    int errorClass = normalPrepareReply.getErrorClass();
                    int returnCode = normalPrepareReply.getReturnCode();

                    if(errorClass != 0)
                    {
                        positionOfSyntaxError_ = normalPrepareReply.getSQLCA().getErrd(5);    //@F10A

                        if(returnCode < 0)
                            JDError.throwSQLException (this, connection_, id_, errorClass, returnCode);
                        else
                            postWarning (JDError.getSQLWarning (connection_, id_, errorClass, returnCode));
                    }

                    // Gather results from the reply.
                    DBDataFormat dataFormat = normalPrepareReply.getDataFormat ();
                    if(dataFormat == null)
                        resultRow = null;
                    else
                        resultRow = new JDServerRow (connection_, id_,
                                                     dataFormat, settings_);

                    //@F5A Don't have to check if a v5r2 system, because extendedMetaData
                    //@F5A can only be true if we are running to v5r2
                    if(extendedMetaData)    //@F5A
                    {
                        //@F5A
                        extendedColumnDescriptors_ = normalPrepareReply.getExtendedColumnDescriptors ();    //@F5A
                    }    //@F5A

                    commonPrepareAfter (sqlStatement, normalPrepareReply);
                }
                catch(DBDataStreamException e)
                {
                    JDError.throwSQLException (JDError.EXC_INTERNAL, e);
                }
                finally
                {    //@P0A
                    if(request != null) {
                    		request.returnToPool();  request=null;
                    }
                    if (normalPrepareReply != null)  { normalPrepareReply.returnToPool(); normalPrepareReply = null; }   //@B5A
                }

                // Output a summary as a trace message.  The * signifies that the
                // statement name comes from the RPB.
                if(JDTrace.isTraceOn())
                    JDTrace.logInformation (this,
                                            "Prepared " + name_ + "*, SQL Statement -->[" + sqlStatement + "]");
            }

        }

        // Check to see if the statement was cancelled.
        if(cancelled_)
            JDError.throwSQLException (JDError.EXC_OPERATION_CANCELLED);

        // If any of the fields is a locator, then we need to turn off             @B2A
        // record blocking.                                                        @B2A
        lastPrepareContainsLocator_ = false;    // @B2A
        if(resultRow != null)
        {    // @B2A
            int fieldCount = resultRow.getFieldCount ();    // @B2A
            for(int i = 1; i <= fieldCount; ++i)
            {    // @B2A
                SQLData sqlData = resultRow.getSQLData(i);
                int sqlType = sqlData.getSQLType();  //@xml3
                if(sqlType == SQLData.CLOB_LOCATOR ||
                     sqlType == SQLData.BLOB_LOCATOR ||
                     sqlType == SQLData.DBCLOB_LOCATOR ||
                     sqlType == SQLData.NCLOB_LOCATOR ||
                     sqlType == SQLData.XML_LOCATOR)     //@xml3

                    lastPrepareContainsLocator_ = true;    // @B2A
            }    // @B2A
        }    // @B2A
      } finally {
         /*@D4A*/
        if (connection_.isQueryTimeoutMechanismCancel()) {
          endCancelThread();
        }
      }
        return resultRow;
    }



    /**
    Performs common operations needed after a prepare.

    @param  sqlStatement    The SQL statement.
    @param  reply           The prepare reply.

    @exception      SQLException    If an error occurs.
    **/
    //
    // This method is intended to be overriden, if needed.
    //
    void commonPrepareAfter (JDSQLStatement sqlStatement,
                             DBReplyRequestedDS reply)    // private protected
    throws SQLException
    {
        currentJDSQLStatement_ = sqlStatement; 
        // Nothing.
    }



    /**
    Performs common operations needed before a prepare.

    @param  sqlStatement    The SQL statement.
    @param  request         The prepare request.

    @exception      SQLException    If an error occurs.
    **/
    //
    // This method is intended to be overriden, if needed.
    //
    void commonPrepareBefore (JDSQLStatement sqlStatement,
                              DBSQLRequestDS request)    // private protected
    throws SQLException
    {
        // Close the result set before preparing another.
        closeResultSet (JDCursor.REUSE_YES);
        currentJDSQLStatement_ = sqlStatement; 

    }



    /**
    Performs common operations in leiu of a prepare.

    @param  sqlStatement    The SQL statement.
    @param  statementIndex  The cached statement index.

    @exception      SQLException    If an error occurs.
    **/
    //
    // This method is intended to be overriden, if needed.
    //
    void commonPrepareBypass (JDSQLStatement sqlStatement,
                              int statementIndex)    // private protected
    throws SQLException
    {
        // Nothing.
      currentJDSQLStatement_ = sqlStatement; 

    }



    /**
    Runs an SQL statement that may return multiple result sets.
    This closes the current result set and clears warnings before
    executing the new SQL statement.

    <p>Under some situations, a single SQL statement may return
    multiple result sets, an update count, or both.  This might occur
    when executing a stored procedure that returns multiple
    result sets or when dynamically executing an unknown SQL string.

    <p>Use getMoreResults(), getResultSet(), and getUpdateCount()
    to navigate through multiple result sets, an update count, or
    both.

    @param  sql     The SQL statement.
    @return         true if a result set was returned, false
                    if an update count was returned or nothing
                    was returned.

    @exception      SQLException    If the statement is not open,
                                    the SQL statement contains a syntax
                                    error, the query timeout limit is
                                    exceeded, or an error occurs.
    **/
    public boolean execute (String sql)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            JDSQLStatement sqlStatement = new JDSQLStatement (sql,
                                                              settings_.getDecimalSeparator (), escapeProcessing_,
                                                              packageCriteria_, connection_);    // @A1A @G4C
            currentJDSQLStatement_ = sqlStatement; 

            if(JDTrace.isTraceOn())    // @D0A
                JDTrace.logInformation (this, "Executing SQL Statement -->[" + sqlStatement + "]");    // @D0A

            // Validate that no parameters are set, since parameters
            // imply the need for a PreparedStatement.
            //
            // Note:  This code causes problems with some statements with a syntax error.
            // For example
            // select * from sysibm.sysdummy1 where 'a = '?'
            //
            // Removing this code to allow database engine errors to be returned.
            //
            // if(sqlStatement.countParameters () > 0)
            //     JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);
            // @AAD

            // Prepare and execute.
            JDServerRow resultRow = commonPrepare (sqlStatement);
            commonExecute (sqlStatement, resultRow);

            return(resultSet_ != null);
        }
    }



    //@G4A JDBC 3.0
    /**
  Runs an SQL statement that may return multiple result sets and
  makes any auto-generated keys available for retrieval using
  Statement.getGeneratedKeys().  This closes the current result set
  and clears warnings before executing the new SQL statement.

  <p>Under some situations, a single SQL statement may return
  multiple result sets, an update count, or both.  This might occur
  when executing a stored procedure that returns multiple
  result sets or when dynamically executing an unknown SQL string.

  <p>Use getMoreResults(), getResultSet(), and getUpdateCount()
  to navigate through multiple result sets, an update count, or
  both.

  <p>This method requires OS/400 V5R2
  or IBM i.  If connecting to OS/400 V5R1 or earlier, an exception
  will be thrown.

  @param  sql               The SQL statement.
  @param  autoGeneratedKeys Indicates whether auto-generated keys should be made available for
                          retrieval.  Valid values are Statement.RETURN_GENERATED_KEYS and
                          Statement.NO_GENERATED_KEYS.
  @return                   true if a result set was returned, false
                          if an update count was returned or nothing
                          was returned.

  @exception      SQLException    If the statement is not open,
                                the SQL statement contains a syntax
                                error, the query timeout limit is
                                exceeded, the value for autoGeneratedKeys is
                                not a valid value, if connecting to OS/400 V5R1
                                or earlier, an error occurs.
  @since Modification 5
  **/

    public boolean execute (String sql, int autoGeneratedKeys)
    throws SQLException
    {
        if(connection_.getVRM() < JDUtilities.vrm520)    //@F5A
            JDError.throwSQLException(JDError.EXC_FUNCTION_NOT_SUPPORTED);    //@F5A

        verifyGeneratedKeysParameter(autoGeneratedKeys);
        autoGeneratedKeys_ = autoGeneratedKeys;

        if((connection_.getVRM() >= JDUtilities.vrm610) && //@GKA
           (autoGeneratedKeys_ == RETURN_GENERATED_KEYS))  //@GKA
        {
            synchronized(internalLock_)
            {
                checkOpen ();
                JDSQLStatement sqlStatement = new JDSQLStatement (sql,
                                                                  settings_.getDecimalSeparator (), escapeProcessing_,
                                                                  packageCriteria_, connection_);
                currentJDSQLStatement_ = sqlStatement; 

                if(JDTrace.isTraceOn())
                    JDTrace.logInformation (this, "Executing SQL Statement -->[" + sqlStatement + "]");

                // Validate that no parameters are set, since parameters
                // imply the need for a PreparedStatement.
                // Note:  This code causes problems with some statements with a syntax error.
                // For example
                // select * from sysibm.sysdummy1 where 'a = '?'
                //
                // Removing this code to allow database engine errors to be returned.
                //
                //if(sqlStatement.countParameters () > 0)
                //    JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);
                // @AAD

                if(sqlStatement.isInsert_)
                {
                    String selectStatement = connection_.makeGeneratedKeySelectStatement(sql);
                    //Construct the new JDSQLStatement object
                    sqlStatement = new JDSQLStatement(selectStatement, settings_.getDecimalSeparator(), escapeProcessing_, packageCriteria_, connection_);
                    if(JDTrace.isTraceOn())
                        JDTrace.logInformation(this, "Generated keys, SQL Statement -->[" + sqlStatement + "]");
                    sqlStatement.setSelectFromInsert(true);
                    currentJDSQLStatement_ = sqlStatement; 

                }

                // Prepare and execute.
                JDServerRow resultRow = commonPrepare (sqlStatement);
                commonExecute (sqlStatement, resultRow);

                return(resultSet_ != null);
            }
        }
        else //@GKA do what we always have
            return execute (sql);
    }



    //@G4A  JDBC 3.0
    /**
    Runs an SQL statement that may return multiple result sets and
    makes any auto-generated keys for the columns indicated in the given array
    available for retrieval using Statement.getGeneratedKeys().
    This closes the current result set and clears warnings before
    executing the new SQL statement.

    <p>Under some situations, a single SQL statement may return
    multiple result sets, an update count, or both.  This might occur
    when executing a stored procedure that returns multiple
    result sets or when dynamically executing an unknown SQL string.

    <p>Use getMoreResults(), getResultSet(), and getUpdateCount()
    to navigate through multiple result sets, an update count, or
    both.

    <p><B>This method is not supported when connecting to IBM i V5R4 or earlier systems.</B>

    @param  sql               The SQL statement.
    @param  columnIndexes     Indicates that auto-generated keys for the indicated
                              columns should be made available for retrieval.
    @return                   true if a result set was returned, false
                              if an update count was returned or nothing
                              was returned.
    @exception      java.sql.SQLException - If connecting to IBM i V5R4 or earlier systems,
                              if the statement is not open,
                              the SQL statement contains a syntax
                              error, the query timeout limit is
                              exceeded, the value for columnIndexes is
                              not a valid value, or an error occurs.
    @since Modification 5
    **/
    public boolean execute (String sql, int[] columnIndexes)
    throws SQLException
    {
        if(connection_.getVRM() >= JDUtilities.vrm610)  //@GKA
        {
            synchronized(internalLock_)
            {
                checkOpen ();
                JDSQLStatement sqlStatement = new JDSQLStatement (sql,
                                                              settings_.getDecimalSeparator (), escapeProcessing_,
                                                              packageCriteria_, connection_);
                currentJDSQLStatement_ = sqlStatement; 

                if(JDTrace.isTraceOn())
                    JDTrace.logInformation (this, "Executing SQL Statement -->[" + sqlStatement + "]");

                // Validate that no parameters are set, since parameters
                // imply the need for a PreparedStatement.
                // Note:  This code causes problems with some statements with a syntax error.
                // For example
                // select * from sysibm.sysdummy1 where 'a = '?'
                //
                // Removing this code to allow database engine errors to be returned.
                //
                // if(sqlStatement.countParameters () > 0)
                //    JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);
                // @AAD

                //check if the statement is an insert statement
                if(sqlStatement.isInsert_)
                {
                    String selectStatement = connection_.makeGeneratedKeySelectStatement(sql, columnIndexes, null);
                    sqlStatement = new JDSQLStatement(selectStatement, settings_.getDecimalSeparator (), escapeProcessing_, packageCriteria_, connection_);
                    currentJDSQLStatement_ = sqlStatement; 
                    if(JDTrace.isTraceOn())
                        JDTrace.logInformation(this, "Generated keys, SQL Statement -->[" + sqlStatement + "]");

                    autoGeneratedKeys_ = RETURN_GENERATED_KEYS;    // specify we want generated keys
                    sqlStatement.setSelectFromInsert(true);
                }
                // Prepare and execute.
                JDServerRow resultRow = commonPrepare (sqlStatement);
                commonExecute (sqlStatement, resultRow);

                return(resultSet_ != null);
            }
        }
        else                                                                        //@GKA
        {
            JDError.throwSQLException (JDError.EXC_FUNCTION_NOT_SUPPORTED);
            return false;
        }
    }



    //@G4A JDBC 3.0
    /**
    Runs an SQL statement that may return multiple result sets and
    makes any auto-generated keys for the columns indicated in the given array
    available for retrieval using Statement.getGeneratedKeys().
    This closes the current result set and clears warnings before
    executing the new SQL statement.

    <p>Under some situations, a single SQL statement may return
    multiple result sets, an update count, or both.  This might occur
    when executing a stored procedure that returns multiple
    result sets or when dynamically executing an unknown SQL string.

    <p>Use getMoreResults(), getResultSet(), and getUpdateCount()
    to navigate through multiple result sets, an update count, or
    both.

    <p><B>This method is not supported when connecting to IBM i V5R4 or earlier systems.</B>

    @param  sql               The SQL statement.
    @param  columnNames     Indicates that auto-generated keys for the indicated
                              columns should be made available for retrieval.
    @return                   true if a result set was returned, false
                              if an update count was returned or nothing
                              was returned.
    @exception      java.sql.SQLException - If connecting to IBM i V5R4 or earlier systems,
                              if the statement is not open,
                              the SQL statement contains a syntax
                              error, the query timeout limit is
                              exceeded, the value for columnNames is
                              not a valid value, or an error occurs.
    @since Modification 5
    **/
    public boolean execute (String sql, String[] columnNames)
    throws SQLException
    {
        if(connection_.getVRM() >= JDUtilities.vrm610)  //@GKA
        {
            synchronized(internalLock_)
            {
                checkOpen ();
                JDSQLStatement sqlStatement = new JDSQLStatement (sql,
                                                              settings_.getDecimalSeparator (), escapeProcessing_,
                                                              packageCriteria_, connection_);
                currentJDSQLStatement_ = sqlStatement; 

                if(JDTrace.isTraceOn())
                    JDTrace.logInformation (this, "Executing SQL Statement -->[" + sqlStatement + "]");

                // Validate that no parameters are set, since parameters
                // imply the need for a PreparedStatement.
                // Note:  This code causes problems with some statements with a syntax error.
                // For example
                // select * from sysibm.sysdummy1 where 'a = '?'
                //
                // Removing this code to allow database engine errors to be returned.
                //
                // if(sqlStatement.countParameters () > 0)
                //    JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);
                // @AAD

                //check if the statement is an insert statement
                if(sqlStatement.isInsert_)
                {
                    String selectStatement = connection_.makeGeneratedKeySelectStatement(sql, null, columnNames);
                    sqlStatement = new JDSQLStatement(selectStatement, settings_.getDecimalSeparator (), escapeProcessing_, packageCriteria_, connection_);
                    currentJDSQLStatement_ = sqlStatement; 

                    if(JDTrace.isTraceOn())
                        JDTrace.logInformation(this, "Generated keys, SQL Statement -->[" + sqlStatement + "]");

                    sqlStatement.setSelectFromInsert(true);
                    autoGeneratedKeys_ = RETURN_GENERATED_KEYS;    // specify we want generated keys
                }
                // Prepare and execute.
                JDServerRow resultRow = commonPrepare (sqlStatement);
                commonExecute (sqlStatement, resultRow);

                return(resultSet_ != null);
            }
        }
        else    // Do what we always have
        {
            JDError.throwSQLException (JDError.EXC_FUNCTION_NOT_SUPPORTED);
            return false;
        }
    }



    // JDBC 2.0
    /**
    Runs the batch of SQL statements.  Batch updates can be used
    to submit a set of SQL statements together as a single unit.
    The SQL statements are run in the order in which they were
    added to the batch.  The batch is cleared after the SQL statements
    are run.  In addition, this closes the current result set and
    clears warnings before executing the new SQL statement.

    <p>When batch updates are run, autocommit should usually be turned off.
    This allows the caller to decide whether or not to commit the
    transaction in the event that an error occurs and some of the
    SQL statements in a batch fail to run.

    @return An array of row counts for the SQL statements that are run.
            The array contains one element for each statement in the
            batch of SQL statements.  The array is ordered according to
            the order in which the SQL statements were added to the batch.

    @exception SQLException If the statement is not open,
                            an SQL statement contains a syntax
                            error, the query timeout limit is
                            exceeded, an SQL statement returns
                            a result set, or an error occurs.
    **/
    //
    // Implementation notes:
    //
    // 1.  We are using a Vector as the main data structure.
    //     Vector is supposedly synchronized already, so we
    //     should not have to worry about synchronization.
    //
    public int[] executeBatch ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            if(batch_ == null) batch_ = new Vector();    //@P0A
            int batchSize = batch_.size ();
            int[] updateCounts = new int[batchSize];

            int i = 0;
            try
            {
                Enumeration list = batch_.elements ();
                while(list.hasMoreElements ())
                {

                    // Prepare and execute.  Check for a result set in
                    // both places.  It is best to catch it after the
                    // prepare (so we don't open a cursor), but with
                    // some stored procedures, we can't catch it until
                    // the execute.
                    JDSQLStatement sqlStatement = (JDSQLStatement) list.nextElement ();
                    currentJDSQLStatement_ = sqlStatement; 

                    if(JDTrace.isTraceOn())    // @D0A
                        JDTrace.logInformation (this, "Executing from batch SQL Statement -->[" + sqlStatement + "]");    // @D0A

                    JDServerRow resultRow = commonPrepare (sqlStatement);
                    if(resultRow != null)
                        JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);

                    commonExecute (sqlStatement, resultRow);
                    if(resultSet_ != null)
                    {
                        closeResultSet (JDCursor.REUSE_YES);
                        JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);
                    }

                    updateCounts[i++] = updateCount_;
                }
            }
            catch(SQLException e)
            {

                // The specification says that if we get an error,
                // then the size of the update counts array should
                // reflect the number of statements that were
                // executed without error.
                int[] updateCounts2 = new int[i];
                System.arraycopy (updateCounts, 0, updateCounts2, 0, i);

                batch_.removeAllElements ();
                BatchUpdateException throwException = new BatchUpdateException (e.getMessage (),
                    e.getSQLState (), e.getErrorCode (), updateCounts2);
                try { 
                  throwException.initCause(e); 
                } catch (Throwable t) {}
                throw throwException;
                }

            batch_.removeAllElements ();
            return updateCounts;
        }
    }



    /**
    Runs an SQL statement that returns a single result set.
    This closes the current result set and clears warnings
    before executing the new SQL statement.

    @param  sql     The SQL statement.
    @return         The result set that contains the data produced
                    by the query.

    @exception      SQLException    If the statement is not open,
                                    the SQL statement contains a syntax
                                    error, no result set is returned by the
                                    database, the query timeout limit is
                                    exceeded, or an error occurs.
    **/
    public ResultSet executeQuery (String sql)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            JDSQLStatement sqlStatement = new JDSQLStatement (sql,
                                                              settings_.getDecimalSeparator (), escapeProcessing_,
                                                              packageCriteria_, connection_);    // @A1A @G4C
            currentJDSQLStatement_ = sqlStatement; 

            if(JDTrace.isTraceOn())    // @D0A
                JDTrace.logInformation (this, "Executing query, SQL Statement -->[" + sqlStatement + "]");    // @D0A

            // Validate that no parameters are set, since parameters
            // imply the need for a PreparedStatement.
            // Note:  This code causes problems with some statements with a syntax error.
            // For example
            // select * from sysibm.sysdummy1 where 'a = '?'
            //
            // Removing this code to allow database engine errors to be returned.
            //
            // if(sqlStatement.countParameters () > 0)
            //    JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);
            // @AAD

            // Prepare and execute.
            JDServerRow resultRow = commonPrepare (sqlStatement);
            commonExecute (sqlStatement, resultRow);

            if((behaviorOverride_ & 1) == 0)    // @F9a
            {
                // @F9a
                if(resultSet_ == null)
                    JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);
            }
            // @F9a
            return resultSet_;
        }
    }



    /**
    Runs an SQL INSERT, UPDATE, or DELETE statement, or any
    SQL statement that does not return a result set.
    This closes the current result set and clears warnings
    before executing the new SQL statement.

    @param  sql     The SQL statement.
    @return         Either the row count for INSERT, UPDATE, or
                    DELETE, or 0 for SQL statements that
                    return nothing.

    @exception      SQLException    If the statement is not open,
                                    the SQL statement contains a syntax
                                    error, the query timeout limit is
                                    exceeded, the statement returns
                                    a result set, or an error occurs.
    **/
    public int executeUpdate (String sql)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            JDSQLStatement sqlStatement = new JDSQLStatement (sql,
                                                              settings_.getDecimalSeparator (), escapeProcessing_,
                                                              packageCriteria_, connection_);    // @A1A @G4A
            currentJDSQLStatement_ = sqlStatement; 


            if(JDTrace.isTraceOn())    // @D0A
                JDTrace.logInformation (this, "Executing update, SQL Statement -->[" + sqlStatement + "]");    // @D0A

            // Validate that no parameters are set, since parameters
            // imply the need for a PreparedStatement.
            // Note:  This code causes problems with some statements with a syntax error.
            // For example
            // select * from sysibm.sysdummy1 where 'a = '?'
            //
            // Removing this code to allow database engine errors to be returned.
            //
            // if(sqlStatement.countParameters () > 0)
            //    JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);
            // @AAD

            // Prepare and execute.  Check for a result set in both
            // places.  It is best to catch it after the prepare (so
            // we don't open a cursor), but with some stored procedures,
            // we can't catch it until the execute.
            JDServerRow resultRow = commonPrepare (sqlStatement);
            if(resultRow != null)
                JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);

            commonExecute (sqlStatement, resultRow);
            if(resultSet_ != null)
            {
                closeResultSet (JDCursor.REUSE_YES);
                JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);
            }

            return updateCount_;
        }
    }



    //@G4A JDBC 3.0
    /**
    Runs an SQL INSERT, UPDATE, or DELETE statement, or any
    SQL statement that does not return a result set and
    makes any auto-generated keys available for retrieval using
    Statement.getGeneratedKeys().
    This closes the current result set and clears warnings
    before executing the new SQL statement.
    <p>This method requires OS/400 V5R2 or
    or IBM i.  If connecting to OS/400 V5R1 or earlier, an exception
    will be thrown.


    @param  sql     The SQL statement.
    @return         Either the row count for INSERT, UPDATE, or
                    DELETE, or 0 for SQL statements that
                    return nothing.

    @exception      SQLException    If the statement is not open,
                                    the SQL statement contains a syntax
                                    error, the query timeout limit is
                                    exceeded, the statement returns
                                    a result set, the value for autoGeneratedKeys is
                                    not a valid value, if connecting to OS/400 V5R1
                                    or earlier, an error occurs.
    @since Modification 5
    **/
    public int executeUpdate (String sql, int autoGeneratedKeys)
    throws SQLException
    {
        if(connection_.getVRM() < JDUtilities.vrm520)    //@F5A
            JDError.throwSQLException(JDError.EXC_FUNCTION_NOT_SUPPORTED);    //@F5A

        verifyGeneratedKeysParameter(autoGeneratedKeys);
        autoGeneratedKeys_ = autoGeneratedKeys;

        if((connection_.getVRM() >= JDUtilities.vrm610) && //@GKA
           (autoGeneratedKeys_ == RETURN_GENERATED_KEYS))  //@GKA
        {
            synchronized(internalLock_)
            {
                checkOpen ();
                JDSQLStatement sqlStatement = new JDSQLStatement (sql,
                                                                  settings_.getDecimalSeparator (), escapeProcessing_,
                                                                  packageCriteria_, connection_);
                currentJDSQLStatement_ = sqlStatement; 

                if(JDTrace.isTraceOn())
                    JDTrace.logInformation (this, "Executing SQL Statement -->[" + sqlStatement + "]");

                // Validate that no parameters are set, since parameters
                // imply the need for a PreparedStatement.
                // Note:  This code causes problems with some statements with a syntax error.
                // For example
                // select * from sysibm.sysdummy1 where 'a = '?'
                //
                // Removed this code let engine return error
                //
                //if(sqlStatement.countParameters () > 0)
                //    JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);
                // @AAD

                if(sqlStatement.isInsert_)
                {
                    String selectStatement = connection_.makeGeneratedKeySelectStatement(sql);
                    //Create a new JDSQLStatement
                    sqlStatement = new JDSQLStatement(selectStatement, settings_.getDecimalSeparator(), escapeProcessing_, packageCriteria_, connection_);
                    if(JDTrace.isTraceOn())
                        JDTrace.logInformation(this, "Generated keys, SQL Statement -->[" + sqlStatement + "]");
                    currentJDSQLStatement_ = sqlStatement; 

                    sqlStatement.setSelectFromInsert(true);
                    // Prepare and execute.
                    JDServerRow resultRow = commonPrepare (sqlStatement);
                    commonExecute (sqlStatement, resultRow);
                }
                else
                {
                    // Prepare and execute.
                    JDServerRow resultRow = commonPrepare (sqlStatement);
                    if(resultRow != null)
                        JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);
                    commonExecute (sqlStatement, resultRow);
                    if(resultSet_ != null)
                    {
                        closeResultSet (JDCursor.REUSE_YES);
                        JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);
                    }
                }
                return updateCount_;
            }
        }
        else //@GKA do what we always have
            return executeUpdate (sql);
    }



    //@G4A JDBC 3.0
    /**
    Runs an SQL INSERT, UPDATE, or DELETE statement, or any
    SQL statement that does not return a result set and
    makes any auto-generated keys for the columns indicated in the given array
    available for retrieval using Statement.getGeneratedKeys().
    This closes the current result set and clears warnings
    before executing the new SQL statement.

    <p><B>This method is not supported when connecting to IBM i V5R4 or earlier systems.</B>

    @param  sql            The SQL statement.
    @param  columnIndexes  The indexes of columns for which auto-generated keys should be made
                           available for retrieval.
    @return         Either the row count for INSERT, UPDATE, or
                    DELETE, or 0 for SQL statements that
                    return nothing.
    @exception      SQLException    If connection to IBM i V5R4 or earlier systems,
                                    the statement is not open,
                                    the SQL statement contains a syntax
                                    error, the query timeout limit is
                                    exceeded, the statement returns
                                    a result set, the value for autoGeneratedKeys is
                                    not a valid value or an error occurs.

    @since Modification 5
    **/
    public int executeUpdate (String sql, int[] columnIndexes)
    throws SQLException
    {
        if(connection_.getVRM() >= JDUtilities.vrm610)   //@GKA
        {
            synchronized(internalLock_)
            {
                //make sure the statement is open
                checkOpen ();

                //create the sql statement object.
                JDSQLStatement sqlStatement = new JDSQLStatement (sql, settings_.getDecimalSeparator (), escapeProcessing_, packageCriteria_, connection_);
                currentJDSQLStatement_ = sqlStatement; 

                if(JDTrace.isTraceOn())
                    JDTrace.logInformation (this, "Executing update, SQL Statement -->[" + sqlStatement + "]");

                // Validate that no parameters are set, since parameters
                // imply the need for a PreparedStatement.
                // Note:  This code causes problems with some statements with a syntax error.
                // For example
                // select * from sysibm.sysdummy1 where 'a = '?'
                //
                // Removing this code to allow database engine errors to be returned.
                //

                //if(sqlStatement.countParameters () > 0)
                //    JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);
                // @AAD

                //check if the statement is an insert statement
                if(sqlStatement.isInsert_)
                {
                    String selectStatement = connection_.makeGeneratedKeySelectStatement(sql, columnIndexes, null);
                    sqlStatement = new JDSQLStatement(selectStatement, settings_.getDecimalSeparator (), escapeProcessing_, packageCriteria_, connection_);
                    currentJDSQLStatement_ = sqlStatement; 
                    if(JDTrace.isTraceOn())
                        JDTrace.logInformation(this, "Generated keys, SQL Statement -->[" + sqlStatement + "]");

                    sqlStatement.setSelectFromInsert(true);
                    autoGeneratedKeys_ = RETURN_GENERATED_KEYS;    // specify we want generated keys
                    // prepare and execute
                    JDServerRow resultRow = commonPrepare(sqlStatement);
                    commonExecute(sqlStatement, resultRow);
                }
                else      // treat just like executeUpdate(String sql) was called
                {
                    // Prepare and execute.  Check for a result set in both
                    // places.  It is best to catch it after the prepare (so
                    // we don't open a cursor), but with some stored procedures,
                    // we can't catch it until the execute.
                    JDServerRow resultRow = commonPrepare (sqlStatement);
                    if(resultRow != null)
                        JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);

                    commonExecute (sqlStatement, resultRow);
                    if(resultSet_ != null)
                    {
                        closeResultSet (JDCursor.REUSE_YES);
                        JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);
                    }
                }
                return updateCount_;
            }
        }
        else
        {
            JDError.throwSQLException (JDError.EXC_FUNCTION_NOT_SUPPORTED);
            return 0;
        }
    }



    //@G4A JDBC 3.0
    /**
    Runs an SQL INSERT, UPDATE, or DELETE statement, or any
    SQL statement that does not return a result set and
    makes any auto-generated keys for the columns indicated in the given array
    available for retrieval using Statement.getGeneratedKeys().
    This closes the current result set and clears warnings
    before executing the new SQL statement.

    <p><B>This method is not supported when connecting to IBM i V5R4 or earlier systems.</B>

    @param  sql          The SQL statement.
    @param  columnNames  The column names for which auto-generated keys should be made
                         available for retrieval.
    @return         Either the row count for INSERT, UPDATE, or
                    DELETE, or 0 for SQL statements that
                    return nothing.
    @exception      SQLException    If connection to IBM i V5R4 or earlier systems,
                                    the statement is not open,
                                    the SQL statement contains a syntax
                                    error, the query timeout limit is
                                    exceeded, the statement returns
                                    a result set, the value for autoGeneratedKeys is
                                    not a valid value or an error occurs.
    @since Modification 5
    **/
    public int executeUpdate (String sql, String[] columnNames)
    throws SQLException
    {
        if(connection_.getVRM() >= JDUtilities.vrm610)                                                                   //@GKA
        {
            synchronized(internalLock_)
            {
                //make sure the statement is open
                checkOpen ();

                //create the sql statement object.
                JDSQLStatement sqlStatement = new JDSQLStatement (sql, settings_.getDecimalSeparator (), escapeProcessing_, packageCriteria_, connection_);
                currentJDSQLStatement_ = sqlStatement; 

                if(JDTrace.isTraceOn())
                    JDTrace.logInformation (this, "Executing update, SQL Statement -->[" + sqlStatement + "]");

                // Validate that no parameters are set, since parameters
                // imply the need for a PreparedStatement.
                // Note:  This code causes problems with some statements with a syntax error.
                // For example
                // select * from sysibm.sysdummy1 where 'a = '?'
                //
                // Removing this code to allow database engine errors to be returned.
                //

                //if(sqlStatement.countParameters () > 0)
                //    JDError.throwSQLException (JDError.EXC_PARAMETER_COUNT_MISMATCH);
                // @AAD

                //check if the statement is an insert statement
                if(sqlStatement.isInsert_)
                {
                    String selectStatement = connection_.makeGeneratedKeySelectStatement(sql, null, columnNames);
                    sqlStatement = new JDSQLStatement(selectStatement, settings_.getDecimalSeparator (), escapeProcessing_, packageCriteria_, connection_);
                    currentJDSQLStatement_ = sqlStatement; 
                    if(JDTrace.isTraceOn())
                        JDTrace.logInformation(this, "Generated keys, SQL Statement -->[" + sqlStatement + "]");

                    sqlStatement.setSelectFromInsert(true);
                    autoGeneratedKeys_ = RETURN_GENERATED_KEYS;    // specify we want generated keys
                    //prepare and execute
                    JDServerRow resultRow = commonPrepare(sqlStatement);
                    commonExecute(sqlStatement, resultRow);
                }
                else      // treat just like executeUpdate(String sql) was called
                {
                    // Prepare and execute.  Check for a result set in both
                    // places.  It is best to catch it after the prepare (so
                    // we don't open a cursor), but with some stored procedures,
                    // we can't catch it until the execute.
                    JDServerRow resultRow = commonPrepare (sqlStatement);
                    if(resultRow != null)
                        JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);

                    commonExecute (sqlStatement, resultRow);
                    if(resultSet_ != null)
                    {
                        closeResultSet (JDCursor.REUSE_YES);
                        JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);
                    }
                }
                return updateCount_;
            }
        }
        else
        {
            JDError.throwSQLException (JDError.EXC_FUNCTION_NOT_SUPPORTED);
            return 0;
        }
    }



    /**
    Closes the statement if not explicitly closed by the caller.

    @exception   Throwable      If an error occurs.
    **/
    protected void finalize ()
    throws Throwable
    {
        try{
            if(! isClosed ()) {
            	JDTrace.logInformation (this, "WARNING: Finalizer thread closing statement object.");
                close ();
            }
        }
        catch(Exception e){
            //catch any exceptions and don't throw them
        }
        super.finalize ();
    }



    /**
    Returns the appropriate blocking factor for the SQL statement.

    @param  sqlStatement    The SQL statement.
    @param  rowLength       The row length.
    @return                 The blocking factor (in rows).
    **/
    int getBlockingFactor (String cursorSensitivityProperty,
                           JDSQLStatement sqlStatement,
                           int rowLength)    // private protected
    {
        boolean block = false;
        boolean useFetchSize = false;
        currentJDSQLStatement_ = sqlStatement; 

        // Allow blocking for asensitive cursors @H1A
        int requestDSCursorType = AS400JDBCResultSet.getDBSQLRequestDSCursorType(cursorSensitivityProperty, resultSetType_, resultSetConcurrency_); 
        // Only block if the cursor is not updatable
        // and no locators are in the result set.                                  @B2A
        if((cursor_.getConcurrency() != ResultSet.CONCUR_UPDATABLE)    // @B2C @EAC
           && (lastPrepareContainsLocator_ == false)
           && (requestDSCursorType  != DBSQLRequestDS.CURSOR_SCROLLABLE_SENSITIVE) &&
           (requestDSCursorType != DBSQLRequestDS.CURSOR_NOT_SCROLLABLE_SENSITIVE))  //@KKB we do not want to block if a sensitive cursor is being used
        {    // @B2A

            // Determine if we should block based on the block
            // criteria and SQL statement characteristics.
            if(blockCriteria_.equalsIgnoreCase (JDProperties.BLOCK_CRITERIA_NONE))
            {
                block = true;
                useFetchSize = true;
                if(JDTrace.isTraceOn()) {
                  JDTrace.logInformation (this, "getBlockingFactor(): Blocking -- blockCriteria_="+blockCriteria_); 
                }
            //
            // The documentation for Statement.setFetchSize() says 
            // that fetchSize is used when the blockSize is 0
            // 
            } else if (fetchSize_ > 1 && blockSize_ == 0) {
              block = true;
              useFetchSize = true;
              if(JDTrace.isTraceOn()) {
                JDTrace.logInformation (this, "getBlockingFactor(): Blocking -- fetchSize="+fetchSize_); 
              }
               
            } else if(sqlStatement != null)  {
                if((blockCriteria_.equalsIgnoreCase (JDProperties.BLOCK_CRITERIA_IF_FETCH))
                   && (sqlStatement.isForFetchOnly())) {
                  
                    block = true;
                    if(JDTrace.isTraceOn()) {
                      JDTrace.logInformation (this, "getBlockingFactor(): Blocking -- blockCriteria_="+blockCriteria_+" and isForFetchOnly"); 
                    }

                }  else if((blockCriteria_.equalsIgnoreCase (JDProperties.BLOCK_CRITERIA_UNLESS_UPDATE))
                        && (! sqlStatement.isForUpdate())) {
                    block = true;
                    if(JDTrace.isTraceOn()) {
                      JDTrace.logInformation (this, "getBlockingFactor(): Blocking -- blockCriteria_="+blockCriteria_+" and isForFetchOnly"); 
                    }
                    
                } else {
                  if(JDTrace.isTraceOn()) {
                    JDTrace.logInformation (this, "getBlockingFactor(): Blocking ="+block+" blockCriteria="+ blockCriteria_); 
                  }
                
                }
            } else {
                if(blockCriteria_.equalsIgnoreCase (JDProperties.BLOCK_CRITERIA_UNLESS_UPDATE)) {
                  block = true;
                } else {
                  if(JDTrace.isTraceOn()) {
                    JDTrace.logInformation (this, "getBlockingFactor(): Blocking ="+block+" blockCriteria="+ blockCriteria_+" sqlStatement is null"); 
                  }
                }
            }
        } else {
          if(JDTrace.isTraceOn()) {
            JDTrace.logInformation (this, 
                  "getBlockingFactor(): Not blocking -- concurrency="+cursor_.getConcurrency()+
                  " lastPrepareContainsLocator_="+lastPrepareContainsLocator_+
                  " requestCursorType="+requestDSCursorType);
          }
        }

        // Compute the blocking factor.
        int blockingFactor;
        if(block)
        {
            if(useFetchSize) {
                blockingFactor = fetchSize_;
                if(JDTrace.isTraceOn()) {
                  JDTrace.logInformation (this, 
                      "getBlockingFactor: blockFactor taken from fetchSize_"); 
                }
                
            }
            else
            {
                if (rowLength <= 0) {
                  blockingFactor = 1;    // Avoid divide by zero -- bug 3119833 
                } else { 
                blockingFactor = (blockSize_ * 1024) / rowLength;
                if(blockingFactor > 32767) { 
                    blockingFactor = 32767;
                } else if(blockingFactor <= 1) {
                    blockingFactor = 1;
                    if(JDTrace.isTraceOn()) {
                      JDTrace.logInformation (this, 
                          "getBlockingFactor: blockFactor is 1: blockSize_="+blockSize_+" rowLength="+rowLength);  
                    }

                }
                }
            }
        } else { 
            blockingFactor = 1;
            if(JDTrace.isTraceOn()) {
              JDTrace.logInformation (this, 
                  "getBlockingFactor: block=false so blockFactor is 1"); 
            }
        }
        if(JDTrace.isTraceOn()) {
          JDTrace.logInformation (this, "getBlockingFactor: blockFactor is "+blockingFactor);  
        }
        return blockingFactor;
    }



    // JDBC 2.0
    /**
    Returns the connection for this statement.

    @return The connection for this statement.

    @exception SQLException If an error occurs.
    **/
    public Connection getConnection ()
    throws SQLException
    {
        return connection_;
    }



    /**
    Returns the name of the SQL cursor that is used by
    subsequent statement execute methods.

    @return     The cursor name.
    **/
    String getCursorName ()
    {
        synchronized(internalLock_)
        {    // @E6A
            return cursor_.getName ();
        }
    }



    //@F3A
    /**
    Returns extended column descriptors for this statement.
    This will be null if the user has the "extended meta data"
    property set to false (the default).

    @return     The extended column descriptors for this statement.
    **/
    /* @P6D
    DBExtendedColumnDescriptors getExtendedColumnDescriptors ()
    {
        synchronized(internalLock_)
        {
            return extendedColumnDescriptors_;
        }
    }
*/ 


    // JDBC 2.0
    /**
    Returns the fetch direction.

    @return The fetch direction.

    @exception SQLException If the statement is not open.
    **/
    public int getFetchDirection ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            return fetchDirection_;
        }
    }



    // JDBC 2.0
    /**
    Returns the fetch size.

    @return The fetch size.

    @exception SQLException If the statement is not open.
    **/
    public int getFetchSize ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            return fetchSize_;
        }
    }



    //@G4A JDBC 3.0
    /**
    Retrieves any auto-generated keys created as a result of executing this Statement object.
    
    <p>In order for this method to return auto-generated keys, the statement must be executed on
    a table with an identity column.  For more information about identity columns, Go to the
    IBM i Information Center, and search on the phrase "identity column".

    <p>This method will return null if the user did not request auto-generated keys
    during the execute of a Statement or during the prepare of a PreparedStatement.
    This method will also return null if the application has not executed the
    statement before calling this method.

    <p>This method will return an empty result set if the database did not generate
    any auto-generated keys when executing the statement.

    @return      The ResultSet object containing the auto-generated key(s)
                 generated by the execution of this Statement object, an empty
                 result set if the database did not generate any auto-generated keys,
                 or null if the application did not request auto-generated keys or
                 did not execute the statement before calling this method.

    @exception SQLException     If the statement is not open,
                                if connecting to OS/400 V5R1 or earlier,
                                an error occurs.
    @since Modification 5
    **/
    public ResultSet getGeneratedKeys ()
    throws SQLException
    {
        synchronized(internalLock_)
        {
            if(connection_.getVRM() < JDUtilities.vrm520)    //@F5A
            {
                //@F5A
                JDError.throwSQLException (JDError.EXC_FUNCTION_NOT_SUPPORTED);    //@F5A
                return null;    //@F5A
            }    //@F5A
            //Check if open because generated key result set only makes sense if the
            //Statement is currently open.
            checkOpen ();

            //@F6 If the user didn't execute the statement yet, throw a function sequence exception.
            //@F6 If it's just an issue of the database not returning any keys,
            //@F6 return an empty result set.
            //@F6 Since we will also have null generated keys if the database did not
            //@F6 return us any values, return null instead of throwing a function sequence error.
            //@F6 This matches the behavior of Statement.getResultSet() if no result set was
            //@F6 created, or getResultSet() is called before executing a statement.
            //@F6D If we have not made any generated keys yet, a statement must
            //@F6D not have been executed, so throw a function sequence error.
            //@F6D if (generatedKeys_ == null)
            //@F6D    JDError.throwSQLException (JDError.EXC_FUNCTION_SEQUENCE);

            return generatedKeys_;
        }
    }



    /**
    Returns the maximum field size limit for the statement.
    The maximum field size limit is the maximum amount of data
    returned for any column value.  It applies only to BINARY,
    VARBINARY, CHAR, and VARCHAR fields.  If the limit is exceeded,
    then the excess data is discarded.

    @return     The maximum field size limit (in bytes), or
                0 for no limit.

    @exception  SQLException    If the statement is not open.
    **/
    public int getMaxFieldSize ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            return maxFieldSize_;
        }
    }



    /**
    Returns the maximum rows limit for the statement.
    The maximum rows limit is the maximum number of rows that a
    result set can contain.  If the limit is exceeded, then the
    excess rows are dropped.

    @return     The maximum rows limit, or 0 for no limit.

    @exception  SQLException    If the statement is not open.
    **/
    public int getMaxRows ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            return maxRows_;
        }
    }



    /**
    Returns the next result set.  This closes the
    current result set.

    @return         true if another result set is available, false
                    if there are no more result sets.

    @exception      SQLException    If an error occurs.
    **/
    public boolean getMoreResults ()
    throws SQLException
    {
     
      
        synchronized(internalLock_)
        {    // @E6A
          
          if(JDTrace.isTraceOn())
            JDTrace.logInformation(this, "getMoreResults() numberOfResults="+numberOfResults_);

            // Initialize.
            cancelled_ = false;
            checkOpen ();
            closeResultSet (JDCursor.REUSE_RESULT_SET);

            // Determine if there more result sets.
            if(--numberOfResults_ > 0)
            {

                // Send the data stream.
                DBSQLRequestDS request = null;    //@P0A
                boolean extendedMetaData = false;  //@P6A
                try
                {
                    if((connection_.getVRM() >= JDUtilities.vrm610)) {
                    	/*@K3A*/
                      int requestedORS = DBSQLRequestDS.ORS_BITMAP_RETURN_DATA+DBSQLRequestDS.ORS_BITMAP_SQLCA+DBSQLRequestDS.ORS_BITMAP_DATA_FORMAT+DBSQLRequestDS.ORS_BITMAP_CURSOR_ATTRIBUTES;
                           extendedMetaData = connection_.getProperties().getBoolean(JDProperties.EXTENDED_METADATA); 
                           if (extendedMetaData)                                                                      
                           {                                                                                          
                               requestedORS = requestedORS + DBSQLRequestDS.ORS_BITMAP_EXTENDED_COLUMN_DESCRIPTORS;    
                           }                                                                                         

                        request = DBDSPool.getDBSQLRequestDS(DBSQLRequestDS.FUNCTIONID_OPEN_DESCRIBE, id_, requestedORS, 0);    //@cur
                        
                    } else {
                        request = DBDSPool.getDBSQLRequestDS(DBSQLRequestDS.FUNCTIONID_OPEN_DESCRIBE, id_, DBSQLRequestDS.ORS_BITMAP_RETURN_DATA+DBSQLRequestDS.ORS_BITMAP_SQLCA+DBSQLRequestDS.ORS_BITMAP_DATA_FORMAT, 0);    //@P0C
                    }
                    int openAttributes = cursor_.getOpenAttributes(null, blockCriteria_);    // @E1A
                    request.setOpenAttributes(openAttributes);    // @E1C

                    //Set the cursor scrollability option if V5R3 or higher, prior to V5R3 we can only be forward only.
                    if(connection_.getVRM() >= JDUtilities.vrm530)
                    {
                        String cursorSensitivity = connection_.getProperties().getString(JDProperties.CURSOR_SENSITIVITY);    //@F8A
                        /* @H1A Use common routine to determine scrollability */  
                        request.setScrollableCursorFlag(AS400JDBCResultSet.getDBSQLRequestDSCursorType(cursorSensitivity, resultSetType_, resultSetConcurrency_));
                    }

                    if (getMoreResultsReply != null) { getMoreResultsReply.returnToPool(); getMoreResultsReply=null; }
                    getMoreResultsReply = connection_.sendAndReceive (request, id_);    //@P0C

                    // Gather information from the reply.
                    // DBReplySQLCA sqlca = getMoreResultsReply.getSQLCA ();
                    DBDataFormat dataFormat = getMoreResultsReply.getDataFormat ();
                    if (extendedMetaData) {                                                 //@P6A
                      DBExtendedColumnDescriptors newDescriptors = getMoreResultsReply
                        .getExtendedColumnDescriptors();                                   //@P6A
                      if (newDescriptors != null) {                                        //@P6A
                        extendedColumnDescriptors_ = newDescriptors;                       //@P6A
                      } else {
                        // There were no extended descriptors.  Do not use a 
                        // stale one. 
                        extendedColumnDescriptors_ = null;                                   //@P6A
                      }                                                                    //@P6A
                    } else {                                                               //@P6A
                      extendedColumnDescriptors_ = null;                                   //@P6A
                    }                                                                      //@P6A

                    // Check for system errors.
                    int errorClass = getMoreResultsReply.getErrorClass();
                    int returnCode = getMoreResultsReply.getReturnCode();

                    if(errorClass != 0)
                    {
                        if(returnCode < 0) {
                            // 
                            // Trace where this statement was created and the other statements
                            // on the connection where created. 
                            // 
                            if (JDTrace.isTraceOn()) {
                              JDTrace.logException(this, "Creation location", creationLocation_); 
                              connection_.dumpStatementCreationLocation(); 
                            }
                            JDError.throwSQLException (this, connection_, id_, errorClass, returnCode);
                        } else {
                            postWarning (JDError.getSQLWarning (connection_, id_, errorClass, returnCode));
                        }
                    }

                    // Do not use the dataFormat until we know there is not an error 
                    if(this instanceof AS400JDBCCallableStatement) {  // @550A
                         // Also make sure the format is not null (prevent NPE)  
                         if (dataFormat == null) { 
                           JDError.throwSQLException (JDError.EXC_INTERNAL,"null dataFormat"); 
                           return false;  // @P6A
                         }
                         dataFormat.setCSRSData(true);       // @550A
                    }
                    
                    cursor_.setExtendedColumnDescriptorsFromReply(getMoreResultsReply);               //@P6A
                    
                    // Process a potential cursor conecurrency override.                             @E1A @EAC
                    cursor_.processConcurrencyOverride(openAttributes, getMoreResultsReply);    // @E1A @EAC

                    cursor_.processCursorAttributes(getMoreResultsReply);                   //@cur

                    // Note that the cursor was opened.
                    cursor_.setState (false);

                    // Compute the result set.
                    JDServerRow row = new JDServerRow (connection_, id_, dataFormat,
                                                       settings_);
                    String cursorSensitivity = connection_.getProperties().getString(JDProperties.CURSOR_SENSITIVITY);    //@F8A
                    
                    JDServerRowCache rowCache = new JDServerRowCache (
                                                                     row, 
                                                                     connection_, id_, 
                                                                     getBlockingFactor (cursorSensitivity,
                                                                                        null, 
                                                                                        row.getRowLength()), 
                                                                     false, 
                                                                     resultSetType_, 
                                                                     cursor_); //@PDC perf //@pda perf2 - fetch/close

                    // If the result set concurrency is updatable, check to                            @E1C
                    // see if the system overrode the cursor type to read only.                        @E1C
                    // Don't worry about the other direction (system overrides                         @E1C
                    // from read-only to updatable).                                                   @E1C
                    int actualConcurrency = (resultSetConcurrency_ == ResultSet.CONCUR_UPDATABLE)    // @E1C
                                            ? cursor_.getConcurrency() : resultSetConcurrency_;    // @E1C @EAC

                    resultSet_ = new AS400JDBCResultSet (this, null,
                                                         rowCache, connection_.getCatalog(),
                                                         cursor_.getName(), maxRows_, resultSetType_,
                                                         actualConcurrency, fetchDirection_,
                                                         fetchSize_, extendedColumnDescriptors_);    // @ECC@P6A
                    if(resultSet_.getConcurrency () != resultSetConcurrency_)
                        postWarning (JDError.getSQLWarning (JDError.WARN_OPTION_VALUE_CHANGED));
                }
                catch(DBDataStreamException e)
                {
                    JDError.throwSQLException (JDError.EXC_INTERNAL, e);
                }
                finally
                {    //@P0A
                    if(request != null)  {
                    	request.returnToPool();    request = null;
                    }
                    if (getMoreResultsReply != null) { getMoreResultsReply.returnToPool(); getMoreResultsReply = null; }
                }
            }

            // Check to see if the statement was cancelled.
            if(cancelled_)
                JDError.throwSQLException (JDError.EXC_OPERATION_CANCELLED);

            // @A0A
            // Added code to set updateCount_ = -1.
            updateCount_ = -1;    // @A0A

            return(resultSet_ != null);
        }
    }



    //@G4A JDBC 3.0
    /**
    Returns the next result set.  This closes the current result set.  This method
    handles any current ResultSet object(s) according to the instructions specified
    by the actionOnCurrentResultSet parameter, and returns true if the next result is a
    ResultSet object.

    @param          current  What should happen to current ResultSet objects
                    obtained using the method getResultSet.  Valid values are
                    CLOSE_CURRENT_RESULT, KEEP_CURRENT_RESULT, and CLOSE_ALL_RESULTS.

    @return         Always false because this method is not supported.

    @exception      SQLException    if DatabaseMetaData.supportsMultipleOpenResults returns false
                    and either Statement.KEEP_CURRENT_RESULT or Statement.CLOSE_ALL_RESULTS are
                    supplied as the argument.
    @since Modification 5
    **/
    public boolean getMoreResults (int current)
    throws SQLException
    {
        if(current == Statement.CLOSE_CURRENT_RESULT)  //@forum1
            return getMoreResults();   //@forum1

        JDError.throwSQLException (JDError.EXC_FUNCTION_NOT_SUPPORTED);
        return false;
    }


    //@F10A
    /**
    Will return the value of the last syntax error that came back from the IBM i system.

    @return     The value of the character of the last syntax error from the system,
                or 0 if no errors occurred or the value is not known.

    @exception  SQLException    If the statement is not open.
    **/
    public int getPositionOfSyntaxError ()
    throws SQLException
    {
        synchronized(internalLock_)
        {
            checkOpen ();
            return positionOfSyntaxError_;
        }
    }


    /**
    Returns the query timeout limit for this statement.
    The query timeout limit is the number of seconds that the
    driver will wait for an SQL statement to execute.

    @return     The query timeout limit (in seconds), or
                0 for no limit.

    @exception  SQLException    If the statement is not open.
    **/
    public int getQueryTimeout ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            return queryTimeout_;
        }
    }



    /**
    Returns the current result set.

    @return     The current result set, or null if an update
                count was returned or there are no more
                result sets.

    @exception  SQLException    If the statement is not open.
    **/
    public ResultSet getResultSet ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            return resultSet_;
        }
    }



    // JDBC 2.0
    /**
    Returns the result set concurrency to be used for this statement.

    @return The result set concurrency.

    @exception  SQLException    If the statement is not open.
    **/
    public int getResultSetConcurrency ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            return resultSetConcurrency_;
        }
    }



    //@G4A  JDBC 3.0
    /**
    Returns the result set cursor holdability to be used for this statement.

    @return     The cursor holdability.  Valid values are ResultSet.HOLD_CURSORS_OVER_COMMIT and
                ResultSet.CLOSE_CURSORS_AT_COMMIT.  The holdability is derived in this order
                of precedence:
                <ul>
                <li>1.  The holdability, if any, that was specified on statement creation using
                the methods createStatement(), prepareCall(), or prepareStatement() on the
                Connection object.
                <li>2.  The holdability specified using the method setHoldability(int)
                if this method was called on the Connection object.
                <li>3.  If neither of above methods were called, the value of the
                <code> cursor hold </code>
                <a href="doc-files/JDBCProperties.html" target="_blank">driver property</a>.</ul>
                Full functionality of #1 and #2 requires OS/400 v5r2
                or IBM i.  If connecting to OS/400 V5R1 or earlier,
                the value specified on these two methods will be ignored and the default holdability
                will be the value of #3.

    @exception  SQLException    If the statement is not open.

    @since Modification 5
    **/
    public int getResultSetHoldability ()
    throws SQLException
    {
        synchronized(internalLock_)
        {
            checkOpen ();

            //if we can get info from resultSet (post 540), that will be more accurate, since it can be from stored-proc cursor
            if((resultSet_ != null) && (!resultSet_.isClosed()))  //@cur //@cur2
            	return resultSet_.getHoldability();   //@cur

            //@F4 If resultSetHoldability_ was set by the user, then return it.  Otherwise,
            //@F4 return the connection's holdability.
            if((resultSetHoldability_ == AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT) ||    //@F4A
               (resultSetHoldability_ == AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT))    //@F4A
            {
                //@F4A
                return resultSetHoldability_;    //@F4A
            }    //@F4A
            return connection_.getHoldability();
        }
    }




    // JDBC 2.0
    /**
    Returns the result set type to be used for this statement.

    @return The result set type.

    @exception  SQLException    If the statement is not open.
    **/
    public int getResultSetType ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            return resultSetType_;
        }
    }



    /**
    Returns the current update count.

    @return     The update count, or -1 if a result set was
                returned or there are no more result sets.

    @exception  SQLException    If the statement is not open.
    **/
    public int getUpdateCount ()
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            return(resultSet_ != null) ? -1 : updateCount_;
        }
    }



    /**
    Returns the first warning reported for the statement.
    Subsequent warnings may be chained to this warning.

    @return     The first warning, or null if no warnings
                have been reported.

    @exception  SQLException    If an error occurs.
    **/
    public SQLWarning getWarnings ()
    throws SQLException
    {
        return sqlWarning_;
    }


    //@KBL
    /*
    Sets whether or not the statement has been partially closed and if the remaining resources
    should be freed when a transaction boundary is reached.
    A statement may become partially closed if the user specified to keep statements associated with
    locators open until a transaction boundary by setting the "hold statements" connection property to true.
    */
    void setHoldStatement(boolean hold){
        holdStatement_ = hold;
    }

    //@KBL
    /*
    Returns whether or not the statement has been partially closed.
    A statement may become partially closed if the user specified to keep statements associated with
    locators open until a transaction boundary by setting the "hold statements" connection property to true.
    */
    boolean isHoldStatement()
    {
        return holdStatement_;
    }

    //@KBL
    /*
    Sets whether or not this statement object has been used to access a locator.
    */
    void setAssociatedWithLocators(boolean hasLocator){
        associatedWithLocators_ = hasLocator;
    }

    //@KBL
    /*
    Returns whether or not this statement object has been used to access a locator.
    */
    boolean isAssociatedWithLocators(){
        return associatedWithLocators_;
    }

    /**
    Indicates if the statement is closed.

    @return     true if the statement is closed;
                false otherwise.
    **/
    public boolean isClosed () throws SQLException //@PDC jdbc40
    {
        return closed_;
    }



    //@G4A
    /**
    Creates a result set to be returned from getGeneratedKeys().

    Make an empty result set if return code is < 0 because the information
    will not be waiting for us in the SQLCA.
    **/
    private void makeGeneratedKeyResultSet(int returnCode, DBReplySQLCA sqlca)
    throws SQLException
    {
        //@F6A Make sure we close the generated key result set.  We don't have to
        //@F6A check to see if the user closed it already because close() does
        //@F6A that for us already.
        if(generatedKeys_ != null)    //@F6A
        {
            //@F6A
            generatedKeys_.close();    //@F6A
        }    //@F6A
        //Code checks we are running to a v5r2 or later system before calling this method
        BigDecimal generatedKey = null;    //@F6C
        if(JDTrace.isTraceOn())
            JDTrace.logInformation (this, "returnCode is: " + returnCode);

        if(returnCode < 0)
        {
            // If SQLCode (returnCode) from system is not 0, then don't go look
            // for auto-generated key because it will not be there.  Instead,
            // make empty result set.
            //@F6D generatedKey = "";
            if(JDTrace.isTraceOn())
                JDTrace.logInformation (this, "return code was not 0, generated key is null");
        }
        else
        {
            // As of right now, we will only be returned the last auto-generated key.
            // Even though it is wasteful to return a ResultSet with only one element,
            // that's what the spec. says we have to do.

            // Consider going to system in the future to fill in the field name.
            // Right now, it doesn't seem like a cost-effective trip.

            // If there is no error, the errmc is instead filled with the auto-generated
            // key for us.
            generatedKey = sqlca.getGeneratedKey();    //@P0C @F6C Deleted converter
            if(JDTrace.isTraceOn())
                JDTrace.logInformation (this, "generated key from system is: " + generatedKey);
            // generatedKey will be null if the database did not return to us a
            // proper generatedKey
        }
        String[] fieldNames = {""};
        SQLData[] sqlData   = { new SQLDecimal (30, 0, settings_, connection_.getVRM(), connection_.getProperties())};    //@F6C // @M0C - changed SQLDecimal ctor signature
        int[] fieldNullables = {AS400JDBCDatabaseMetaData.columnNoNulls};
        JDSimpleRow formatRow = new JDSimpleRow (fieldNames, sqlData, fieldNullables);
        boolean[][] nulls = {{false}};
        boolean[][] dataMappingErrors = {{false}};
        JDRowCache rowCache = null;
        if(generatedKey == null)
        {
            rowCache = new JDSimpleRowCache(formatRow);
        }
        else
        {
            Object[][] data = {{generatedKey}};
            rowCache = new JDSimpleRowCache (formatRow, data, nulls, dataMappingErrors);
        }
        // Construct with row cache, no catalog, and no cursor name
        generatedKeys_ = new AS400JDBCResultSet (rowCache, "", "", connection_, null); //@in2
    }



    // @B3A
    /**
    Marks the cursor as closed.

    @param isRollback True if we called this from rollback(), false if we called this from commit().
    **/
    void markCursorClosed(boolean isRollback)    //@F4C
    throws SQLException    //@F2A
    {
        // @K94 - See if the user requested to hold cursors after a rollback
        boolean rollbackCursorHold = connection_.getProperties().getBoolean(JDProperties.ROLLBACK_CURSOR_HOLD); //@K94
        if(rollbackCursorHold)     //@K94
            isRollback = false;    //@K94

        // @F4 Added a check of statement level holdability because the user can specify the
        // @F4 holdability of a specific statement (as a different value than the connection
        // @F4 holdability) with JDBC 3.0 support.  If this was called from Connection.rollback(),
        // @F4 close all statements.
        if((getResultSetHoldability() == AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT    //@F4A
           || isRollback)  //@F4A
           &&  ((connection_.getVRM() <= JDUtilities.vrm610)
               || ((connection_.getVRM() >= JDUtilities.vrm710) && cursor_.getCursorIsolationLevel() != 0)))   //@isol only close if cursor's isolationlvl is not *none
        {
            //@F5D cursor_.setState(true);
            //@F4 Instead of calling closeResultSet which does more work than we need to,
            //@F4 just move the code here that we need to clean up our internal variables.
            //@F4 Otherwise, we'd make an unnecessary flow to the system to tell it to
            //@F4 close the cursor which it already has.
            if(resultSet_ != null)    //@F4A
            {
                //@F4A
                if(! resultSet_.isClosed ())    //@F4A
                    resultSet_.close ();    //@F4A
                resultSet_ = null;    //@F4A
            }    //@F4A
            updateCount_ = -1;    //@F4A
        }
    }




    /**
    Notifies the statement that its result set has been closed.

    @exception  SQLException    If an error occurs.
    **/
    void notifyClose ()
    throws SQLException
    {
        // The result set is close, so we can close the cursor.
        if(! cursor_.isClosed ())
        {
            if(numberOfResults_ > 1) {
                cursor_.close (JDCursor.REUSE_RESULT_SET);
            } else {
               // Only one result set.
                cursor_.close (JDCursor.REUSE_YES);
            }
        }

        // Close statement if closeOnCompletion_ and all results have been processed.
        if (closeOnCompletion_ && numberOfResults_ <= 1) { // @D7A
          this.close();
        }
    }



    /**
    Posts a warning for the statement.

    @param   sqlWarning  The warning.
    **/
    void postWarning (SQLWarning sqlWarning)
    {
        /* check to see if warning should be ignored */ 
        if( connection_ != null && connection_.ignoreWarning(sqlWarning)) {
            if (JDTrace.isTraceOn ())           {
                JDTrace.logInformation(this, "postWarning("+sqlWarning+") -- ignored");
             }
        } else { 
          if (JDTrace.isTraceOn ())           {
            JDTrace.logInformation(this, "postWarning("+sqlWarning+")");
         }

        if(sqlWarning_ == null)
            sqlWarning_ = sqlWarning;
        else
            sqlWarning_.setNextWarning (sqlWarning);
        }
    }



    // @E9C
    /**
    Sets the SQL cursor name that will be used by the statement.
    Cursor names must be unique within the connection.
    The cursor name can only be set when no result set is open.

    <p>The cursor name can be used in SQL positioned UPDATE or DELETE
    statements to identify the current row in the result set generated
    by this statement.  By definition, SQL positioned UPDATEs or
    DELETEs must be executed by a different statement than the one
    that generated the result set being used for positioning.

    <p>Cursor names are case sensitive.  However, when using a cursor
    name within other SQL positioned UPDATE or DELETE statements,
    the cursor name will be uppercased.  If you use a cursor name
    with lowercase characters, you need to enclose it in double
    quotes when referring to it in other SQL statements.

    @param  cursorName    The cursor name.  If null, a unique
                          default name will be used.

    @exception      SQLException    If the statement is not open,
                                    a result set is open, the cursor
                                    name is not unique within the
                                    connection, or an error occurs.
    **/
    //
    // Implementation note:
    //
    // If you set the cursor name to a mixed case string, and then
    // later refer to it in an SQL statement, SQL will choke because it
    // will have uppercased the string.
    //
    // At first I thought we should always uppercase, the cursor name
    // but that limits callers.  The better solution is to have the callers
    // surround mixed case cursor names in SQL statements with double
    // quotes.
    //
    public void setCursorName (String cursorName)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();

            // Check that no cursor is open for this statement.
            // We can not just check the cursor, because if the
            // caller closes the result set, we do not close the
            // cursor right away.
            if(resultSet_ != null)
            {
                if(! resultSet_.isClosed ())
                    JDError.throwSQLException (JDError.EXC_CURSOR_STATE_INVALID);
                else
                    closeResultSet (JDCursor.REUSE_YES);
            }

            // If name is null, then use the default name.
            if(cursorName == null)
                cursorName = cursorDefaultName_;

            // Validate the length of the cursor name.
            int cursorNameLength = cursorName.length();    // @EEA
            int maxLength = (connection_.getVRM() >= JDUtilities.vrm610) ? MAX_CURSOR_NAME_LENGTH : MAX_CURSOR_NAME_LENGTH_PRE_V6R1;    //@550 128 byte cursor name support
            if((cursorNameLength > maxLength) || (cursorNameLength == 0))    // @EEC    @550 changed MAX_CURSOR_NAME_LENGTH to maxLength
                JDError.throwSQLException (JDError.EXC_CURSOR_NAME_INVALID);

            // If the name is the same as the current cursor name,
            // then nothing needs to be done.  (If we go through
            // with the change and the cursor name is the same, it
            // will get flagged as ambiguous).
            if(! cursorName.equals (cursor_.getName ()))
            {

                // Check that the cursor name is unique within the
                // connection.
                if(connection_.isCursorNameUsed (cursorName))
                    JDError.throwSQLException (JDError.EXC_CURSOR_NAME_AMBIGUOUS);

                cursor_.setName (cursorName);

                // Since we store the cursor name in the RPB, we need
                // to sync it after a change.
                rpbSyncNeeded_ = true;

            }
        }
    }



    /**
    Sets the escape processing mode.  When processing escape
    clauses, the JDBC driver substitutes escape clauses
    in SQL statements with DB2 for IBM i SQL grammar elements.
    If escape processing is not needed, then setting the escape
    processing mode to false improves performance.

    @param  escapeProcessing      true to process escape clauses;
                                  false otherwise.  The default
                                  is true.

    @exception          SQLException    If the statement is not open.
    **/
    public void setEscapeProcessing (boolean escapeProcessing)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();
            escapeProcessing_ = escapeProcessing;

            if(JDTrace.isTraceOn())
                JDTrace.logProperty (this, "set..", "Escape processing", escapeProcessing_);
        }
    }



    // JDBC 2.0
    /**
    Sets the direction in which the rows in a result set will be
    processed.

    <p>This setting is not used.

    @param      fetchDirection  The fetch direction for processing rows.
                                Valid values are:
                                <ul>
                                  <li>ResultSet.FETCH_FORWARD
                                  <li>ResultSet.FETCH_REVERSE
                                  <li>ResultSet.FETCH_UNKNOWN
                                </ul>
                                The default is ResultSet.FETCH_FORWARD.

    @exception          SQLException    If the statement is not open,
                                        the result set type is
                                        ResultSet.TYPE_FORWARD_ONLY,
                                        and the input value is not
                                        ResultSet.FETCH_FORWARD,
                                        or the input value is not valid.
    **/
    public void setFetchDirection (int fetchDirection)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            if(((fetchDirection != ResultSet.FETCH_FORWARD)
                && (fetchDirection != ResultSet.FETCH_REVERSE)
                && (fetchDirection != ResultSet.FETCH_UNKNOWN))
               || ((resultSetType_ == ResultSet.TYPE_FORWARD_ONLY)
                   && (fetchDirection != ResultSet.FETCH_FORWARD)))
                JDError.throwSQLException (JDError.EXC_ATTRIBUTE_VALUE_INVALID);

            checkOpen ();
            fetchDirection_ = fetchDirection;

            if(JDTrace.isTraceOn())
                JDTrace.logProperty (this, "set..", "Fetch direction", fetchDirection_);
        }
    }



    // JDBC 2.0
    /**
    Sets the number of rows to 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 driver will choose an appropriate fetch size.

    <p>This setting only affects statements that meet the criteria
    specified in the "block criteria" property.  The fetch size
    is only used if the "block size" property is set to "0".
    
    <p>This setting only takes effect for result sets that are opened
    after this method has been called.  Ideally, this method should be
    called before the statement is executed. 
    

    @param fetchSize    The number of rows.  This must be greater than
                        or equal to 0 and less than or equal to the
                        maximum rows limit. The default is zero.

    @exception          SQLException    If the statement is not open
                                        or the input value is not valid.
    **/
    public void setFetchSize (int fetchSize)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            if((fetchSize < 0)
               || ((fetchSize > maxRows_) && (maxRows_ > 0)))
                JDError.throwSQLException (JDError.EXC_ATTRIBUTE_VALUE_INVALID);

            checkOpen ();
            fetchSize_ = fetchSize;

            if(JDTrace.isTraceOn())
                JDTrace.logProperty (this, "set..", "Fetch size", fetchSize_);
            
            
        }
    }



    /**
    Sets the maximum field size limit.  The maximum field size
    limit is the maximum amount of data returned for any column
    value.  It applies only to BINARY, VARBINARY, CHAR, and VARCHAR
    fields.  If the limit is exceeded, then the excess data is
    discarded.

    @param  maxFieldSize     The maximum field size limit (in bytes) or
                             0 for no limit. The default is 0.

    @exception      SQLException    If the statement is not open
                                    or the input value is not valid.
    **/
    public void setMaxFieldSize (int maxFieldSize)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();

            // Validate the argument.
            if(maxFieldSize < 0)
                JDError.throwSQLException (JDError.EXC_ATTRIBUTE_VALUE_INVALID);

            maxFieldSize_ = maxFieldSize;
            settings_ = SQLConversionSettings.getConversionSettingsWithMaxFieldSize(settings_,maxFieldSize);

            if (parameterRow_ != null) { 
              parameterRow_.updateSettings(settings_); 
            }
            if(JDTrace.isTraceOn())
                JDTrace.logProperty (this, "set..", "Max field size", maxFieldSize_);
        }
    }



    /**
    Sets the maximum rows limit.  The maximum rows limit is the
    maximum number of rows that a result set can contain.
    If the limit is exceeded, the excess rows are dropped.


    @param  maxRows     The maximum rows limit or 0 for no limit.
                        The default is 0.

    @exception      SQLException    If the statement is not open
                                    or the input value is not valid.
    **/
    //
    // Implementation note:
    //
    // We had a debate about what this means with respect to scrollable
    // result sets.  Does it limit the number of rows in the result set,
    // or the number of rows fetched.  I.e., if you set the max rows to
    // 50, can you successfully do absolute(51)?  It is only fetching
    // 1 row.  The answer came back from the JavaSoft people that it
    // does indeed limit the number of rows in the result set, not
    // necessarily the number of fetches.
    //
    public void setMaxRows (int maxRows)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();

            // Validate the argument.
            if(maxRows < 0)
                JDError.throwSQLException (JDError.EXC_ATTRIBUTE_VALUE_INVALID);

            maxRows_ = maxRows;
            longMaxRows_ = maxRows; 
            
            if(JDTrace.isTraceOn())
                JDTrace.logProperty (this, "set..", "Max rows", maxRows_);
        }
    }



    /**
    Sets the query timeout limit.  The query timeout limit
    is the number of seconds that the driver will wait for a
    SQL statement to execute.

    <p>This is implemented using the database query time limit, also
    known as QQRYTIMLMT.  This value specifies the query processing time limit that is
    compared to the estimated number of elapsed seconds that a query must run. The
    time limit determines if the database query can start.

    <p> Beginning with Version 6 Release 1 of IBM i, you must have *JOBCTL special authority.

    @param  queryTimeout    The query timeout limit (in seconds)
                            or 0 for no limit.  The default is the job's query timeout limit
                   value unless this method is explicitly called.

    @exception      SQLException    If the statement is not open
                                    or the input value is not valid.
    **/
    public void setQueryTimeout (int queryTimeout)
    throws SQLException
    {
        synchronized(internalLock_)
        {    // @E6A
            checkOpen ();

            // Validate the argument.
            if(queryTimeout < 0)
                JDError.throwSQLException (JDError.EXC_ATTRIBUTE_VALUE_INVALID);

            if((queryTimeout_ != queryTimeout) || !queryTimeoutSet_)  /*@B2C*/
            {
            	queryTimeoutSet_ = true;                              /*@B2A*/
                queryTimeout_ = queryTimeout;

                // Since we store the query timeout in the RPB, we need
                // to sync it after a change.
                rpbSyncNeeded_ = true;
                rpbQueryTimeoutChanged_ = true;    //@EFA
            }

            if(JDTrace.isTraceOn())
                JDTrace.logProperty (this, "set..", "Query timeout", queryTimeout_);
        }
    }



    /**
    Creates or changes the RPB (Request Parameter Block) for the
    statement.

    @exception  SQLException    If an error occurs.
    **/
    private void syncRPB ()
    throws SQLException
    {
        if(rpbSyncNeeded_)
        {

            int functionId = rpbCreated_ ? DBSQLRPBDS.FUNCTIONID_CHANGE_RPB
                             : DBSQLRPBDS.FUNCTIONID_CREATE_RPB;

            DBSQLRPBDS request = null;    //@P0A
            try
            {
                request = DBDSPool.getDBSQLRPBDS(functionId, id_, 0, 0);    //@P0C

                request.setPrepareStatementName (name_, connection_.converter_);    //@P0C
                request.setCursorName (cursor_.getName (), connection_.converter_);    //@P0C

                if(packageManager_.isEnabled())
                    request.setLibraryName (packageManager_.getLibraryName (), connection_.converter_);    //@P0C

                if(rpbQueryTimeoutChanged_)    //@EFA
                {
                   // Don't set the query timeout in the request if using the cancel query timeout mechanism @D4C
                    if(queryTimeout_ > 0 && !connection_.isQueryTimeoutMechanismCancel())
                        request.setQueryTimeout (queryTimeout_);
                    else
                        request.setQueryTimeout (-1);
                }

                if(! connection_.getMustSpecifyForUpdate())    // @J3a @J31c
                {
                    // @J3a
                    if(resultSetConcurrency_ == ResultSet.CONCUR_UPDATABLE)    // @J3a
                        request.setOpenAttributes(0x10 + 0x20 + 0x40 + 0x80);    // @J3a
                }    // @J3a

                //@F5D Do not need to specify this on the RPB
                //@F5D Check system level before sending new code point
                //@F5D if (connection_.getVRM() >= JDUtilities.vrm520)                                    // @F4A
                //@F5D {
                //@F5D     if (resultSetHoldability_ == AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT)       // @F4A
                //@F5D         request.setResultSetHoldabilityOption((byte)0xD5);                         // @F4A
                //@F5D     else if (resultSetHoldability_ == AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT) // @F4A
                //@F5D         request.setResultSetHoldabilityOption((byte)0xE8);                         // @F4A
                //@F5D}

                // @F6A - Send the RPB describe option
                String rpbDescOpt = connection_.getProperties().getString(JDProperties.DESCRIBE_OPTION);
                if(null != rpbDescOpt && rpbDescOpt.length() > 0){
                    if(rpbDescOpt.equals(JDProperties.DESCRIBE_OPTION_SYSTEM_NAMES)){
                        request.setDescribeOption(0xE2);
                    } else if(rpbDescOpt.equals(JDProperties.DESCRIBE_OPTION_LONG_NAMES)){
                        request.setDescribeOption(0xD5);
                    }else if(rpbDescOpt.equals(JDProperties.DESCRIBE_OPTION_LABELS)){
                        request.setDescribeOption(0xD3);
                    }
                }

                connection_.send (request, id_);
            }
            catch(DBDataStreamException e)
            {
                JDError.throwSQLException (JDError.EXC_INTERNAL, e);
            }
            finally
            {    //@P0A
                if(request != null) {
                	request.returnToPool();   request = null;
                }
            }

            rpbCreated_ = true;
            rpbSyncNeeded_ = false;
        }
    }



    /**
    Returns the statement name.

    @return     The statement name.
    **/
    public String toString ()
    {
        return name_;
    }


    /**
    Verifies values for the autoGeneratedKey parameter.

    @param  autoGeneratedKeys Valid values are Statement.RETURN_GENERATED_KEYS and
                              Statement.NO_GENERATED_KEYS.
    @exception  SQLException    If an error occurs.
    **/
    static final void verifyGeneratedKeysParameter (int autoGeneratedKeys)
    throws SQLException
    {
        if((autoGeneratedKeys != NO_GENERATED_KEYS)
           && (autoGeneratedKeys != RETURN_GENERATED_KEYS))
            JDError.throwSQLException (JDError.EXC_ATTRIBUTE_VALUE_INVALID);
    }


    //@PDA jdbc40
    /**
     * Requests that a <code>Statement</code> be pooled or not pooled.  The value
     * specified is a hint to the statement pool implementation indicating
     * whether the applicaiton wants the statement to be pooled.  It is up to
     * the statement pool manager as to whether the hint is used.
     * <p>
     * The poolable value of a statement is applicable to both internal
     * statement caches implemented by the driver and external statement caches
     * implemented by application servers and other applications.
     * <p>
     * By default, a <code>Statement</code> is poolable when it is created.
     * <p>
     * @param poolable      requests that the statement be pooled if true and
     *                      that the statement not be pooled if false
     * <p>
     * @throws SQLException if the <code>Statement</code> has been closed
     * <p>
     */
    public void setPoolable(boolean poolable) throws SQLException
    {
        checkOpen();//@pda do same as native
        isPoolable_ = poolable;
    }

    //@PDA jdbc40
    /**
     * Returns the value of the statements poolable hint, indicating whether
     * pooling of the statement is requested.
     * <p>
     * @return      The value of the statements poolable hint.
     * <p>
     * @throws SQLException if the <code>Statement</code> has been closed
     * <p>
     * see java.sql.Statement#setPoolable(boolean) setPoolable(boolean)
     */
    public boolean isPoolable() throws SQLException
    {
        checkOpen();//@pda do same as native
        return isPoolable_;
    }

    //@pda jdbc40
    protected String[] getValidWrappedList()
    {
        return new String[] {  "com.ibm.as400.access.AS400JDBCStatement", "java.sql.Statement" };
    }

    //  @D7A
    /**
     * Specifies that this Statement will be closed when all its dependent result sets are closed.
     * If execution of the Statement does not produce any result sets, this method has no effect.
     * <p>Note: Multiple calls to closeOnCompletion do not toggle the effect on this Statement.
     * However, a call to closeOnCompletion does effect both the subsequent execution of statements,
     * and statements that currently have open, dependent, result sets.
     * @throws SQLException - if this method is called on a closed Statement
     */

    public void closeOnCompletion() throws SQLException {
      checkOpen();
      closeOnCompletion_ = true;
    }
/**
 * Returns a value indicating whether this Statement will be closed when all its dependent result sets are closed.
 * @return true if the Statement will be closed when all of its dependent result sets are closed; false otherwise
 * @throws SQLException - if this method is called on a closed Statement
 */

    // @D7A
    public boolean isCloseOnCompletion() throws SQLException {
      checkOpen();
      return closeOnCompletion_;
    }

    /*@D4A*/
    /**
    Handles the work involved in supporting a setQueryTimeout option with the cancel property set. .

    **/
        protected void startCancelThread()
        {
            // Start a cancel thread if there is a query running and a timeout value has been
            // specified.
            if (queryTimeoutSet_ && queryTimeout_ != 0) {

                // We only are going to allow the query timeout to effect queries.
                // Set a flag that a query is running.


                    queryRunning_ = true;

                    // Create a thread to do the cancel if needed.  Start the thread.
                    cancelThread_ = new AS400JDBCQueryCancelThread(this);

                    // Uncommented this @A7A ..
                    // this is needed to prevent job from hanging if the extra threads
                    // we started don't end.
                    //
                    cancelThread_.setDaemon(true);

                    cancelThread_.start();
            }
        }



    /*@D4A*/
    /**
     * endTheCancelThread
    **/
        protected void endCancelThread()
        {
            // Deal with the cancel thread at this point.
            if (queryTimeout_ != 0) {

                    // Set the flag saying the query is no longer running.
                    queryRunning_ = false;

                    // Detach the thread from the statement.
                    cancelThread_.statement_ = null;

                    // Interrupt the thread so that it wakes up and dies.
                    cancelThread_.interrupt();
            }
        }

/**
 * <p>Retrieves the current result as an update count; if the result is a ResultSet object 
 * or there are no more results, -1 is returned. This method should be called only 
 * once per result.
 * <p>This method should be used when the returned row count may exceed Integer.MAX_VALUE.
 *
 * @return the current result as an update count; -1 if the current result is a ResultSet 
 * object or there are no more results
 * @throws SQLException - if a database access error occurs or this method is 
 * called on a closed Statement
 */
    public   long getLargeUpdateCount() throws SQLException {
      return (long) getUpdateCount(); 
    }
    
    /**
     *<p>Sets the limit for the maximum number of rows that any ResultSet object 
     *generated by this Statement object can contain to the given number. 
     *If the limit is exceeded, the excess rows are silently dropped.
     *<p>This method should be used when the row limit may exceed Integer.MAX_VALUE. 
     * @param  max the new max rows limit; zero means there is no limit
     * @throws SQLException - if a database access error occurs, this 
     * method is called on a closed Statement or the 
     * condition max >= 0 is not satisfied
     */
    public void setLargeMaxRows(long max) throws SQLException {
      int intMax = (int) max; 
      // If we overflow, just set it to no limit
      if (max > 0x7FFFFFFF) {
        intMax = 0; 
      }
      setMaxRows(intMax);
      longMaxRows_ = max; 
    }
    /**
     *Retrieves the maximum number of rows that a ResultSet object produced by this 
     *Statement object can contain. If this limit is exceeded, the excess rows 
     *are silently dropped.
     * <p> This method should be used when the returned row limit may exceed Integer.MAX_VALUE. 
     * @return the current maximum number of rows for a ResultSet object produced by this 
     *  means there is no limit
     * @throws SQLException -if a database access error occurs or this method is 
     * called on a closed Statement
     */
  public long getLargeMaxRows() throws SQLException {
    checkOpen ();
    return longMaxRows_; 
  }

  /**
   * <p>Submits a batch of commands to the database for execution and if all commands 
   * execute successfully, returns an array of update counts. The long elements of 
   * the array that is returned are ordered to correspond to the commands in the batch,
   *  which are ordered according to the order in which they were added to the batch. 
   *  The elements in the array returned by the method executeLargeBatch may be one of
   *   the following:
   *   
   *   <p>A number greater than or equal to zero -- indicates that the command was
   *   processed successfully and is an update count giving the number of rows in
   *   the database that were affected by the command's execution
   *   <p>A value of SUCCESS_NO_INFO -- indicates that the command was processed 
   *   successfully but that the number of rows affected is unknown.
   *   
   *   <p>If one of the commands in a batch update fails to execute properly,
   *   this method throws a BatchUpdateException, and a JDBC driver may or may 
   * not continue to process the remaining commands in the batch. However, 
   * the driver's behavior must be consistent with a particular DBMS, 
   * either always continuing to process commands or never continuing to 
   * process commands. If the driver continues processing after a failure, 
   * the array returned by the method BatchUpdateException.getLargeUpdateCounts
   *  will contain as many elements as there are commands in the batch, and 
   *  at least one of the elements will be the following:
   *
   * <p>A value of EXECUTE_FAILED -- indicates that the command failed to execute 
   * successfully and occurs only if a driver continues to process commands 
   * after a command fails
   *
   * <p>This method should be used when the returned row count may exceed 
   * Integer.MAX_VALUE.
   * @return an array of update counts containing one element for each command in the 
   * batch. The elements of the array are ordered according to the order in which 
   * commands were added to the batch.
   * @throws SQLException - if a database access error occurs, this method is called 
   * on a closed Statement or the driver does not support batch statements. 
   * Throws BatchUpdateException (a subclass of SQLException) if one of the commands 
   * sent to the database fails to execute properly or attempts to return a result set.
   * @throws SQLTimeoutException - when the driver has determined that the timeout value
   *  that was specified by the setQueryTimeout method has been exceeded and has at least
   *   attempted to cancel the currently running Statement
   */
  
  public long[] executeLargeBatch()    throws SQLException  {
   
   int[] results = executeBatch(); 
   long[] longResults = new long[results.length];
   for (int i = 0; i < results.length; i++) { 
     longResults[i] = results[i]; 
   }
   return longResults; 
 }

/**
 * Executes the given SQL statement, which may be an INSERT, UPDATE, or DELETE 
 * statement or an SQL statement that returns nothing, such as an SQL DDL 
 * statement.
 * <p>This method should be used when the returned row count may exceed 
 * Integer.MAX_VALUE.
 * <p>Note:This method cannot be called on a PreparedStatement or CallableStatement.
 * @param sql an SQL Data Manipulation Language (DML) statement, such as INSERT, 
 * UPDATE or DELETE; or an SQL statement that returns nothing, such as a DDL 
 * statement.
 * @return either (1) the row count for SQL Data Manipulation Language (DML) 
 * statements or (2) 0 for SQL statements that return nothing
 * @throws SQLException - if a database access error occurs, this method is 
 * called on a closed Statement, the given SQL statement produces a ResultSet 
 * object, the method is called on a PreparedStatement or CallableStatement.
 * @throws SQLTimeoutException - when the driver has determined that the 
 * timeout value that was specified by the setQueryTimeout method has been 
 * exceeded and has at least attempted to cancel the currently running 
 * Statement
 */
 public long executeLargeUpdate(String sql)   throws SQLException {
   return (long) executeUpdate(sql); 
 }

/**
 * Executes 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. The driver will ignore the flag if the SQL 
 * statement is not an INSERT statement, or an SQL statement able to return 
 * auto-generated keys (the list of such statements is vendor-specific).
 * <p>This method should be used when the returned row count may exceed 
 * Integer.MAX_VALUE.
 * @param sql - an SQL Data Manipulation Language (DML) statement, such as 
 * INSERT, UPDATE or DELETE; or an SQL statement that returns nothing, such 
 * as a DDL statement.
 * @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 (1) the row count for SQL Data Manipulation Language (DML) 
 * statements or (2) 0 for SQL statements that return nothing
 * @throws SQLException - if a database access error occurs, this method is 
 * called on a closed Statement, the given SQL statement returns a ResultSet 
 * object, the given constant is not one of those allowed, the method is 
 * called on a PreparedStatement or CallableStatement
 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not 
 * support this method with a constant of Statement.RETURN_GENERATED_KEYS
 * @throws SQLTimeoutException - when the driver has determined that the 
 * timeout value that was specified by the setQueryTimeout method has been 
 * exceeded and has at least attempted to cancel the currently running 
 * Statement
 */

  public long executeLargeUpdate(String sql,
                                  int autoGeneratedKeys)
                           throws SQLException {
    return (long) executeUpdate(sql, autoGeneratedKeys); 
  }

/**
 * 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. 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 SQL statement is not an INSERT statement, or an SQL statement able 
 * to return auto-generated keys (the list of such statements is vendor-specific).
 * <p>This method should be used when the returned row count may exceed 
 * Integer.MAX_VALUE.
 * <p>Note:This method cannot be called on a PreparedStatement or CallableStatement.  
 * @param sql - an SQL Data Manipulation Language (DML) statement, such as INSERT, 
 * UPDATE or DELETE; or an SQL statement that returns nothing, such as a DDL 
 * statement.
 * @param columnIndexes - an array of column indexes indicating the columns that 
 * should be returned from the inserted row
 * @return either (1) the row count for SQL Data Manipulation Language (DML) 
 * statements or (2) 0 for SQL statements that return nothing
 * @throws SQLException - if a database access error occurs, this method is called
 * on a closed Statement, the SQL statement returns a ResultSet object,the second 
 * argument supplied to this method is not an int array whose elements are valid 
 * column indexes, the method is called on a PreparedStatement or CallableStatement
 * @throws SQLFeatureNotSupportedException - if the JDBC driver does not support 
 * this method
 * @throws SQLTimeoutException - when the driver has determined that the timeout 
 * value that was specified by the setQueryTimeout method has been exceeded and 
 * has at least attempted to cancel the currently running Statement
 * 
 */

  public long executeLargeUpdate(String sql,
                                  int[] columnIndexes) 
                           throws SQLException {
    return (long) executeUpdate(sql, columnIndexes); 
  }

/**
 *   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. 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 SQL 
 *     statement is not an INSERT statement, or an SQL statement able 
 *     to return auto-generated keys (the list of such statements is 
 *     vendor-specific).
 * <p>  This method should be used when the returned row count may exceed 
 * Integer.MAX_VALUE.
 * <p> Note:This method cannot be called on a PreparedStatement or 
 * CallableStatement.
 * @param sql - an SQL Data Manipulation Language (DML) statement, such as 
 * INSERT, UPDATE or DELETE; or an SQL statement that returns nothing, 
 * such as a DDL statement.
 * @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 statements, 
 * or 0 for SQL statements that return nothing
 * @throws SQLException  if a database access error occurs, this method 
 * is called on a closed Statement, the SQL statement returns a ResultSet 
 * object, the second argument supplied to this method is not a String 
 * array whose elements are valid column names, the method is called on a 
 * PreparedStatement or CallableStatement
 * @throws SQLFeatureNotSupportedException - if the JDBC driver does 
 * not support this method
 *  @throws SQLTimeoutException - when the driver has determined that 
 *  the timeout value that was specified by the setQueryTimeout method 
 *  has been exceeded and has at least attempted to cancel the 
 *  currently running Statement
  */
  public long executeLargeUpdate(String sql,
                                  String[] columnNames)  throws SQLException {
    return (long) executeUpdate(sql, columnNames); 
    
  }
             
  JDSQLStatement getJDSQLStatement() {
    return currentJDSQLStatement_ ;  
  }

}
