com.splicemachine.db.impl.drda.DRDAConnThread.java Source code

Java tutorial

Introduction

Here is the source code for com.splicemachine.db.impl.drda.DRDAConnThread.java

Source

/*
 * This file is part of Splice Machine.
 * Splice Machine is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3, or (at your option) any later version.
 * Splice Machine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License along with Splice Machine.
 * If not, see <http://www.gnu.org/licenses/>.
 *
 * Some parts of this source code are based on Apache Derby, and the following notices apply to
 * Apache Derby:
 *
 * Apache Derby is a subproject of the Apache DB project, and is licensed under
 * the Apache License, Version 2.0 (the "License"); you may not use these files
 * except in compliance with the License. You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * Splice Machine, Inc. has modified the Apache Derby code in this file.
 *
 * All such Splice Machine modifications are Copyright 2012 - 2017 Splice Machine, Inc.,
 * and are licensed to you under the GNU Affero General Public License.
 */

package com.splicemachine.db.impl.drda;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.PrivilegedAction;
import java.sql.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Properties;
import java.util.TimeZone;
import java.util.Vector;
import java.util.regex.Pattern;

import com.splicemachine.db.catalog.SystemProcedures;
import com.splicemachine.db.iapi.error.StandardException;
import com.splicemachine.db.iapi.error.ExceptionSeverity;
import com.splicemachine.db.iapi.reference.Attribute;
import com.splicemachine.db.iapi.reference.DRDAConstants;
import com.splicemachine.db.iapi.reference.JDBC30Translation;
import com.splicemachine.db.iapi.reference.Property;
import com.splicemachine.db.iapi.reference.SQLState;
import com.splicemachine.db.iapi.services.info.JVMInfo;
import com.splicemachine.db.iapi.services.monitor.Monitor;
import com.splicemachine.db.iapi.services.sanity.SanityManager;
import com.splicemachine.db.iapi.services.stream.HeaderPrintWriter;
import com.splicemachine.db.iapi.types.SQLRowId;
import com.splicemachine.db.iapi.tools.i18n.LocalizedResource;
import com.splicemachine.db.iapi.jdbc.AuthenticationService;
import com.splicemachine.db.iapi.jdbc.EngineLOB;
import com.splicemachine.db.iapi.jdbc.EngineResultSet;
import com.splicemachine.db.impl.jdbc.EmbedSQLException;
import com.splicemachine.db.impl.jdbc.Util;
import com.splicemachine.db.jdbc.InternalDriver;
import com.splicemachine.db.iapi.jdbc.EnginePreparedStatement;
import org.apache.hadoop.security.UserGroupInformation;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.Oid;

import javax.security.auth.Subject;

/**
 * This class translates DRDA protocol from an application requester to JDBC
 * for Derby and then translates the results from Derby to DRDA
 * for return to the application requester.
 */
class DRDAConnThread extends Thread {

    private static final Pattern PARSE_TIMESTAMP_PATTERN = Pattern.compile("[-.]");

    private static final String leftBrace = "{";
    private static final String rightBrace = "}";
    private static final byte NULL_VALUE = (byte) 0xff;
    private static final String SYNTAX_ERR = "42X01";

    // Manager Level 3 constant.
    private static final int MGRLVL_3 = 0x03;

    // Manager Level 4 constant.
    private static final int MGRLVL_4 = 0x04;

    // Manager Level 5 constant.
    private static final int MGRLVL_5 = 0x05;

    // Manager level 6 constant.
    private static final int MGRLVL_6 = 0x06;

    // Manager Level 7 constant.
    private static final int MGRLVL_7 = 0x07;

    // Commit or rollback UOWDSP values
    private static final int COMMIT = 1;
    private static final int ROLLBACK = 2;

    private int correlationID;
    private InputStream sockis;
    private OutputStream sockos;
    private DDMReader reader;
    private DDMWriter writer;
    private DRDAXAProtocol xaProto;

    private static int[] ACCRDB_REQUIRED = { CodePoint.RDBACCCL, CodePoint.CRRTKN, CodePoint.PRDID,
            CodePoint.TYPDEFNAM, CodePoint.TYPDEFOVR };

    private static int MAX_REQUIRED_LEN = 5;

    private int currentRequiredLength = 0;
    private int[] required = new int[MAX_REQUIRED_LEN];

    private NetworkServerControlImpl server; // server who created me
    private Session session; // information about the session
    private long timeSlice; // time slice for this thread
    private Object timeSliceSync = new Object(); // sync object for updating time slice 
    private boolean logConnections; // log connections to databases

    private boolean sendWarningsOnCNTQRY = false; // Send Warnings for SELECT if true
    private Object logConnectionsSync = new Object(); // sync object for log connect
    private boolean close; // end this thread
    private Object closeSync = new Object(); // sync object for parent to close us down
    private static HeaderPrintWriter logStream;
    private AppRequester appRequester; // pointer to the application requester
    // for the session being serviced
    private Database database; // pointer to the current database
    private int sqlamLevel; // SQLAM Level - determines protocol

    // DRDA diagnostic level, DIAGLVL0 by default 
    private byte diagnosticLevel = (byte) 0xF0;

    // manager processing
    private Vector unknownManagers;
    private Vector knownManagers;
    private Vector errorManagers;
    private Vector errorManagersLevel;

    // database accessed failed
    private SQLException databaseAccessException;

    // these fields are needed to feed back to jcc about a statement/procedure's PKGNAMCSN
    /** The value returned by the previous call to
     * <code>parsePKGNAMCSN()</code>. */
    private Pkgnamcsn prevPkgnamcsn = null;
    /** Current RDB Package Name. */
    private DRDAString rdbnam = null;
    /** Current RDB Collection Identifier. */
    private DRDAString rdbcolid = null;
    /** Current RDB Package Identifier. */
    private DRDAString pkgid = null;
    /** Current RDB Package Consistency Token. */
    private DRDAString pkgcnstkn = null;
    /** Current RDB Package Section Number. */
    private int pkgsn;

    private final static String TIMEOUT_STATEMENT = "SET STATEMENT_TIMEOUT ";

    private int pendingStatementTimeout; // < 0 means no pending timeout to set

    // this flag is for an execute statement/procedure which actually returns a result set;
    // do not commit the statement, otherwise result set is closed

    // for decryption
    private static DecryptionManager decryptionManager;

    // public key generated by Deffie-Hellman algorithm, to be passed to the encrypter,
    // as well as used to initialize the cipher
    private byte[] myPublicKey;

    // contexts for Kerberos authentication
    private GSSContext gssContext;
    private UserGroupInformation user;

    // generated target seed to be used to generate the password substitute
    // as part of SECMEC_USRSSBPWD security mechanism
    private byte[] myTargetSeed;

    // Some byte[] constants that are frequently written into messages. It is more efficient to 
    // use these constants than to convert from a String each time 
    // (This replaces the qryscraft_ and notQryscraft_ static exception objects.)
    private static final byte[] eod00000 = { '0', '0', '0', '0', '0' };
    private static final byte[] eod02000 = { '0', '2', '0', '0', '0' };
    private static final byte[] nullSQLState = { ' ', ' ', ' ', ' ', ' ' };
    private static final byte[] errD4_D6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // 12x0 
    private static final byte[] warn0_warnA = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; // 11x ' '

    private final static String AUTHENTICATION_PROVIDER_BUILTIN_CLASS = "com.splicemachine.db.impl.jdbc.authentication.BasicAuthenticationServiceImpl";

    private final static String AUTHENTICATION_PROVIDER_NONE_CLASS = "com.splicemachine.db.impl.jdbc.authentication.NoneAuthenticationServiceImpl";

    // Work around a classloader bug involving interrupt handling during
    // class loading. If the first request to load the
    // DRDAProtocolExceptionInfo class occurs during shutdown, the
    // loading of the class may be aborted when the Network Server calls
    // Thread.interrupt() on the DRDAConnThread. By including a static
    // reference to the DRDAProtocolExceptionInfo class here, we ensure
    // that it is loaded as soon as the DRDAConnThread class is loaded,
    // and therefore we know we won't be trying to load the class during
    // shutdown. See DERBY-1338 for more background, including pointers
    // to the apparent classloader bug in the JVM.
    private static final DRDAProtocolExceptionInfo dummy = new DRDAProtocolExceptionInfo(0, 0, 0, false);
    /**
     * Tells if the reset / connect request is a deferred request.
     * This information is used to work around a bug (DERBY-3596) in a
     * compatible manner, which also avoids any changes in the client driver.
     * <p>
     * The bug manifests itself when a connection pool data source is used and
     * logical connections are obtained from the physical connection associated
     * with the data source. Each new logical connection causes a new physical
     * connection on the server, including a new transaction. These connections
     * and transactions are not closed / cleaned up.
     */
    private boolean deferredReset = false;

    private final static String SPLICE_ODBC_NAME = "Splice Enhanced ODBC Driver";

    public boolean canCompress() {
        return session.canCompress();
    }

    private void parsePRDDTA() throws DRDAProtocolException {
        if (reader.getDdmLength() > CodePoint.MAX_NAME)
            tooBig(CodePoint.PRDDTA);
        byte[] prd = reader.readBytes();

        if (prd.length == 0)
            badObjectLength(CodePoint.PRDDTA);

        String prdDTA = reader.convertBytes(prd);
        if (prdDTA.compareTo(SPLICE_ODBC_NAME) == 0)
            session.enableCompress(true);

        if (!appRequester.srvclsnm.equals("QDERBY/JVM")) {
            // make sure this request is coming from the splice driver
            if (!prdDTA.contains("Splice")) {
                invalidClient(appRequester.prdid);
            }
        }
    }

    // constructor
    /**
     * Create a new Thread for processing session requests
     *
     * @param session Session requesting processing
     * @param server  Server starting thread
     * @param timeSlice timeSlice for thread
     * @param logConnections
     **/

    DRDAConnThread(Session session, NetworkServerControlImpl server, long timeSlice, boolean logConnections) {

        super();

        // Create a more meaningful name for this thread (but preserve its
        // thread id from the default name).
        NetworkServerControlImpl.setUniqueThreadName(this, "DRDAConnThread");

        this.session = session;
        this.server = server;
        this.timeSlice = timeSlice;
        this.logConnections = logConnections;
        this.pendingStatementTimeout = -1;
        initialize();
    }

    /**
     * Main routine for thread, loops until the thread is closed
     * Gets a session, does work for the session
     */
    public void run() {
        if (SanityManager.DEBUG)
            trace("Starting new connection thread");

        Session prevSession;
        while (!closed()) {

            // get a new session
            prevSession = session;
            session = server.getNextSession(session);
            if (session == null)
                close();

            if (closed())
                break;
            if (session != prevSession) {
                initializeForSession();
            }
            try {
                long timeStart = System.currentTimeMillis();

                switch (session.state) {
                case Session.INIT:
                    sessionInitialState();
                    if (session == null)
                        break;
                    // else fallthrough
                case Session.ATTEXC:
                case Session.SECACC:
                case Session.CHKSEC:
                    long currentTimeSlice;

                    do {
                        try {
                            processCommands();
                        } catch (DRDASocketTimeoutException ste) {
                            // Just ignore the exception. This was
                            // a timeout on the read call in
                            // DDMReader.fill(), which will happen
                            // only when timeSlice is set.
                        }
                        currentTimeSlice = getTimeSlice();
                    } while ((currentTimeSlice <= 0)
                            || (System.currentTimeMillis() - timeStart < currentTimeSlice));

                    break;
                default:
                    // this is an error
                    agentError("Session in invalid state:" + session.state);
                }
            } catch (Exception e) {
                if (e instanceof DRDAProtocolException && ((DRDAProtocolException) e).isDisconnectException()) {
                    // client went away - this is O.K. here
                    closeSession();
                } else {
                    handleException(e);
                }
            } catch (Error error) {
                // Do as little as possible, but try to cut loose the client
                // to avoid that it hangs in a socket read-call.
                // TODO: Could make use of Throwable.addSuppressed here when
                //       compiled as Java 7 (or newer).
                try {
                    closeSession();
                } catch (Throwable t) {
                    // One last attempt...
                    try {
                        session.clientSocket.close();
                    } catch (IOException ioe) {
                        // Ignore, we're in deeper trouble already.
                    }
                } finally {
                    // Rethrow the original error, ignore errors that happened
                    // when trying to close the socket to the client.
                    throw error;
                }
            }
        }
        if (SanityManager.DEBUG)
            trace("Ending connection thread");
        server.removeThread(this);

    }

    /**
     * Get input stream
     *
     * @return input stream
     */
    protected InputStream getInputStream() {
        return sockis;
    }

    /**
     * Get output stream
     *
     * @return output stream
     */
    protected OutputStream getOutputStream() {
        return sockos;
    }

    /**
     *  get DDMReader
     * @return DDMReader for this thread
     */
    protected DDMReader getReader() {
        return reader;
    }

    /** 
     * get  DDMWriter 
     * @return DDMWriter for this thread
     */
    protected DDMWriter getWriter() {
        return writer;
    }

    /**
     * Get correlation id
     *
     * @return correlation id
     */
    protected int getCorrelationID() {
        return correlationID;
    }

    /**
     * Get session we are working on
     *
     * @return session
     */
    protected Session getSession() {
        return session;
    }

    /**
     * Get Database we are working on
     *
     * @return database
     */
    protected Database getDatabase() {
        return database;
    }

    /**
     * Get server
     *
     * @return server
     */
    protected NetworkServerControlImpl getServer() {
        return server;
    }

    /**
     * Get correlation token
     *
     * @return crrtkn
     */
    protected byte[] getCrrtkn() {
        if (database != null)
            return database.crrtkn;
        return null;
    }

    /**
     * Get database name
     *
     * @return database name
     */
    protected String getDbName() {
        if (database != null)
            return database.getDatabaseName();
        return null;
    }

    /**
     * Close DRDA  connection thread
     */
    protected void close() {
        synchronized (closeSync) {
            close = true;
        }
    }

    /**
     * Set logging of connections
     * 
     * @param value value to set for logging connections
     */
    protected void setLogConnections(boolean value) {
        synchronized (logConnectionsSync) {
            logConnections = value;
        }
    }

    /**
     * Set time slice value
     *
     * @param value new value for time slice
     */
    protected void setTimeSlice(long value) {
        synchronized (timeSliceSync) {
            timeSlice = value;
        }
    }

    /**
     * Indicate a communications failure
     * 
     * @param arg1 - info about the communications failure
     * @param arg2 - info about the communications failure
     * @param arg3 - info about the communications failure
     * @param arg4 - info about the communications failure
     *
     * @exception DRDAProtocolException  disconnect exception always thrown
     */
    protected void markCommunicationsFailure(String arg1, String arg2, String arg3, String arg4)
            throws DRDAProtocolException {
        markCommunicationsFailure(null, arg1, arg2, arg3, arg4);

    }

    /**
     * Indicate a communications failure. Log to db.log
     * 
     * @param e  - Source exception that was thrown
     * @param arg1 - info about the communications failure
     * @param arg2 - info about the communications failure
     * @param arg3 - info about the communications failure
     * @param arg4 - info about the communications failure
     *
     * @exception DRDAProtocolException  disconnect exception always thrown
     */
    protected void markCommunicationsFailure(Exception e, String arg1, String arg2, String arg3, String arg4)
            throws DRDAProtocolException {
        String dbname = null;

        if (database != null) {
            dbname = database.getDatabaseName();
        }
        if (e != null) {
            println2Log(dbname, session.drdaID, e.getMessage());
            server.consoleExceptionPrintTrace(e);
        }

        Object[] oa = { arg1, arg2, arg3, arg4 };
        throw DRDAProtocolException.newDisconnectException(this, oa);
    }

    /**
     * Syntax error
     *
     * @param errcd      Error code
     * @param cpArg  code point value
     * @exception DRDAProtocolException
     */

    protected void throwSyntaxrm(int errcd, int cpArg) throws DRDAProtocolException {
        throw new DRDAProtocolException(DRDAProtocolException.DRDA_Proto_SYNTAXRM, this, cpArg, errcd);
    }

    /**
     * Agent error - something very bad happened
     *
     * @param msg   Message describing error
     *
     * @exception DRDAProtocolException  newAgentError always thrown
     */
    protected void agentError(String msg) throws DRDAProtocolException {

        String dbname = null;
        if (database != null)
            dbname = database.getDatabaseName();
        throw DRDAProtocolException.newAgentError(this, CodePoint.SVRCOD_PRMDMG, dbname, msg);
    }

    /**
     * Missing code point
     *
     * @param codePoint  code point value
     * @exception DRDAProtocolException
     */
    protected void missingCodePoint(int codePoint) throws DRDAProtocolException {
        throwSyntaxrm(CodePoint.SYNERRCD_REQ_OBJ_NOT_FOUND, codePoint);
    }

    /**
     * Print a line to the DB2j log
     *
     * @param dbname  database name
     * @param drdaID   DRDA identifier
     * @param msg   message
     */
    protected static void println2Log(String dbname, String drdaID, String msg) {
        if (logStream == null)
            logStream = Monitor.getStream();

        if (dbname != null) {
            int endOfName = dbname.indexOf(';');
            if (endOfName != -1)
                dbname = dbname.substring(0, endOfName);
        }
        logStream.printlnWithHeader("(DATABASE = " + dbname + "), (DRDAID = " + drdaID + "), " + msg);
    }

    /**
     * Write RDBNAM
     *
     * @param rdbnam    database name
     * @exception DRDAProtocolException
     */
    protected void writeRDBNAM(String rdbnam) throws DRDAProtocolException {
        CcsidManager currentManager = writer.getCurrentCcsidManager();

        int len = currentManager.getByteLength(rdbnam);
        if (len < CodePoint.RDBNAM_LEN)
            len = CodePoint.RDBNAM_LEN;

        /* Write the string padded */
        writer.writeScalarPaddedString(CodePoint.RDBNAM, rdbnam, len);
    }

    /***************************************************************************
     *                   Private methods
     ***************************************************************************/

    /**
     * Initialize class
     */
    private void initialize() {
        // set input and output sockets
        // this needs to be done before creating reader
        sockis = session.sessionInput;
        sockos = session.sessionOutput;

        reader = new DDMReader(this, session.dssTrace);
        writer = new DDMWriter(this, session.dssTrace);

        /* At this stage we can initialize the strings as we have
         * the CcsidManager for the DDMWriter. */
        rdbnam = new DRDAString(writer);
        rdbcolid = new DRDAString(writer);
        pkgid = new DRDAString(writer);
        pkgcnstkn = new DRDAString(writer);
    }

    /**
     * Initialize for a new session
     */
    private void initializeForSession() {
        // set input and output sockets
        sockis = session.sessionInput;
        sockos = session.sessionOutput;

        // intialize reader and writer
        reader.initialize(this, session.dssTrace);
        writer.reset(session.dssTrace);

        // initialize local pointers to session info
        database = session.database;
        appRequester = session.appRequester;

        // set sqlamLevel
        if (session.state == Session.ATTEXC)
            sqlamLevel = appRequester.getManagerLevel(CodePoint.SQLAM);

        /* All sessions MUST start as EBCDIC */
        reader.setEbcdicCcsid();
        writer.setEbcdicCcsid();
    }

    /**      
     * In initial state for a session, 
     * determine whether this is a command
     * session or a DRDA protocol session.  A command session is for changing
     * the configuration of the Net server, e.g., turning tracing on
     * If it is a command session, process the command and close the session.
     * If it is a DRDA session, exchange server attributes and change session
     * state.
     */
    private void sessionInitialState() throws Exception {
        // process NetworkServerControl commands - if it is not either valid protocol  let the 
        // DRDA error handling handle it
        if (reader.isCmd()) {
            try {
                server.processCommands(reader, writer, session);
                // reset reader and writer
                reader.initialize(this, null);
                writer.reset(null);
                closeSession();
            } catch (Throwable t) {
                if (t instanceof InterruptedException)
                    throw (InterruptedException) t;
                else {
                    server.consoleExceptionPrintTrace(t);
                }
            }

        } else {
            // exchange attributes with application requester
            exchangeServerAttributes();
        }
    }

    /**
     * Cleans up and closes a result set if an exception is thrown
     * when collecting QRYDTA in response to OPNQRY or CNTQRY.
     *
     * @param stmt the DRDA statement to clean up
     * @param sqle the exception that was thrown
     * @param writerMark start index for the first DSS to clear from
     * the output buffer
     * @exception DRDAProtocolException if a DRDA protocol error is
     * detected
     */
    private void cleanUpAndCloseResultSet(DRDAStatement stmt, SQLException sqle, int writerMark)
            throws DRDAProtocolException {
        if (stmt != null) {
            writer.clearDSSesBackToMark(writerMark);
            if (!stmt.rsIsClosed()) {
                try {
                    stmt.rsClose();
                } catch (SQLException ec) {
                    if (SanityManager.DEBUG) {
                        trace("Warning: Error closing result set");
                    }
                }
                writeABNUOWRM();
                writeSQLCARD(sqle, CodePoint.SVRCOD_ERROR, 0, 0);
            }
        } else {
            writeSQLCARDs(sqle, 0);
        }
        errorInChain(sqle);
    }

    /**
     * Process DRDA commands we can receive once server attributes have been
     * exchanged.
     *
     * @exception DRDAProtocolException
     */
    private void processCommands() throws DRDAProtocolException {
        DRDAStatement stmt = null;
        int updateCount = 0;
        boolean PRPSQLSTTfailed = false;
        boolean checkSecurityCodepoint = session.requiresSecurityCodepoint();
        do {
            correlationID = reader.readDssHeader();
            int codePoint = reader.readLengthAndCodePoint(false);
            int writerMark = writer.markDSSClearPoint();

            if (checkSecurityCodepoint)
                verifyInOrderACCSEC_SECCHK(codePoint, session.getRequiredSecurityCodepoint());

            switch (codePoint) {
            case CodePoint.CNTQRY:
                try {
                    stmt = parseCNTQRY();
                    if (stmt != null) {
                        writeQRYDTA(stmt);
                        if (stmt.rsIsClosed()) {
                            writeENDQRYRM(CodePoint.SVRCOD_WARNING);
                            writeNullSQLCARDobject();
                        }
                        // Send any warnings if JCC can handle them
                        checkWarning(null, null, stmt.getResultSet(), 0, false, sendWarningsOnCNTQRY);
                        writePBSD();
                    }
                } catch (SQLException e) {
                    // if we got a SQLException we need to clean up and
                    // close the result set Beetle 4758
                    cleanUpAndCloseResultSet(stmt, e, writerMark);
                }
                break;
            case CodePoint.EXCSQLIMM:
                try {
                    updateCount = parseEXCSQLIMM();
                    // RESOLVE: checking updateCount is not sufficient
                    // since it will be 0 for creates, we need to know when
                    // any logged changes are made to the database
                    // Not getting this right for JCC is probably O.K., this
                    // will probably be a problem for ODBC and XA
                    // The problem is that JDBC doesn't provide this information
                    // so we would have to expand the JDBC API or call a
                    // builtin method to check(expensive)
                    // For now we will assume that every execute immediate
                    // does an update (that is the most conservative thing)
                    if (database.RDBUPDRM_sent == false) {
                        writeRDBUPDRM();
                    }

                    // we need to set update count in SQLCARD
                    checkWarning(null, database.getDefaultStatement().getStatement(), null, updateCount, true,
                            true);
                    writePBSD();
                } catch (SQLException e) {
                    writer.clearDSSesBackToMark(writerMark);
                    writeSQLCARDs(e, 0);
                    errorInChain(e);
                }
                break;

            case CodePoint.EXCSQLSET:
                try {
                    if (parseEXCSQLSET())
                        // all went well.
                        writeSQLCARDs(null, 0);
                } catch (SQLWarning w) {
                    writeSQLCARD(w, CodePoint.SVRCOD_WARNING, 0, 0);
                } catch (SQLException e) {
                    writer.clearDSSesBackToMark(writerMark);
                    writeSQLCARDs(e, 0);
                    errorInChain(e);
                }
                break;

            case CodePoint.PRPSQLSTT:
                int sqldaType;
                PRPSQLSTTfailed = false;
                try {
                    database.getConnection().clearWarnings();
                    sqldaType = parsePRPSQLSTT();
                    database.getCurrentStatement().sqldaType = sqldaType;
                    if (sqldaType > 0) // do write SQLDARD
                        writeSQLDARD(database.getCurrentStatement(), (sqldaType == CodePoint.TYPSQLDA_LIGHT_OUTPUT),
                                database.getConnection().getWarnings());
                    else
                        checkWarning(database.getConnection(), null, null, 0, true, true);

                } catch (SQLException e) {
                    writer.clearDSSesBackToMark(writerMark);
                    writeSQLCARDs(e, 0, true);
                    PRPSQLSTTfailed = true;
                    errorInChain(e);
                }
                break;
            case CodePoint.OPNQRY:
                PreparedStatement ps = null;
                try {
                    if (PRPSQLSTTfailed) {
                        // read the command objects
                        // for ps with parameter
                        // Skip objects/parameters
                        skipRemainder(true);

                        // If we failed to prepare, then we fail
                        // to open, which  means OPNQFLRM.
                        writeOPNQFLRM(null);
                        break;
                    }
                    Pkgnamcsn pkgnamcsn = parseOPNQRY();
                    if (pkgnamcsn != null) {
                        stmt = database.getDRDAStatement(pkgnamcsn);
                        ps = stmt.getPreparedStatement();
                        ps.clearWarnings();
                        if (pendingStatementTimeout >= 0) {
                            ps.setQueryTimeout(pendingStatementTimeout);
                            pendingStatementTimeout = -1;
                        }
                        stmt.execute();
                        writeOPNQRYRM(false, stmt);
                        checkWarning(null, ps, null, 0, false, true);

                        long sentVersion = stmt.versionCounter;
                        long currentVersion = ((EnginePreparedStatement) stmt.ps).getVersionCounter();

                        if (stmt.sqldaType == CodePoint.TYPSQLDA_LIGHT_OUTPUT && currentVersion != sentVersion) {
                            // DERBY-5459. The prepared statement has a
                            // result set and has changed on the server
                            // since we last informed the client about its
                            // shape, so re-send metadata.
                            //
                            // NOTE: This is an extension of the standard
                            // DRDA protocol since we send the SQLDARD
                            // even if it isn't requested in this case.
                            // This is OK because there is already code on the
                            // client to handle an unrequested SQLDARD at
                            // this point in the protocol.
                            writeSQLDARD(stmt, true, null);
                        }
                        writeQRYDSC(stmt, false);

                        stmt.rsSuspend();

                        if (stmt.getQryprctyp() == CodePoint.LMTBLKPRC && stmt.getQryrowset() != 0) {
                            // The DRDA spec allows us to send
                            // QRYDTA here if there are no LOB
                            // columns.
                            DRDAResultSet drdars = stmt.getCurrentDrdaResultSet();
                            try {
                                if (drdars != null && !drdars.hasLobColumns()) {
                                    writeQRYDTA(stmt);
                                }
                            } catch (SQLException sqle) {
                                cleanUpAndCloseResultSet(stmt, sqle, writerMark);
                            }
                        }
                    }
                    writePBSD();
                } catch (SQLException e) {
                    writer.clearDSSesBackToMark(writerMark);
                    // The fix for DERBY-1196 removed code 
                    // here to close the prepared statement 
                    // if OPNQRY failed.
                    writeOPNQFLRM(e);
                }
                break;
            case CodePoint.RDBCMM:
                try {
                    if (SanityManager.DEBUG)
                        trace("Received commit");
                    if (!database.getConnection().getAutoCommit()) {
                        database.getConnection().clearWarnings();
                        database.commit();
                        writeENDUOWRM(COMMIT);
                        checkWarning(database.getConnection(), null, null, 0, true, true);
                    }
                    // we only want to write one of these per transaction
                    // so set to false in preparation for next command
                    database.RDBUPDRM_sent = false;
                } catch (SQLException e) {
                    writer.clearDSSesBackToMark(writerMark);
                    // Even in case of error, we have to write the ENDUOWRM.
                    writeENDUOWRM(COMMIT);
                    writeSQLCARDs(e, 0);
                    errorInChain(e);
                }
                break;
            case CodePoint.RDBRLLBCK:
                try {
                    if (SanityManager.DEBUG)
                        trace("Received rollback");
                    database.getConnection().clearWarnings();
                    database.rollback();
                    writeENDUOWRM(ROLLBACK);
                    checkWarning(database.getConnection(), null, null, 0, true, true);
                    // we only want to write one of these per transaction
                    // so set to false in preparation for next command
                    database.RDBUPDRM_sent = false;
                } catch (SQLException e) {
                    writer.clearDSSesBackToMark(writerMark);
                    // Even in case of error, we have to write the ENDUOWRM.
                    writeENDUOWRM(ROLLBACK);
                    writeSQLCARDs(e, 0);
                    errorInChain(e);
                }
                break;
            case CodePoint.CLSQRY:
                try {
                    stmt = parseCLSQRY();
                    stmt.rsClose();
                    writeSQLCARDs(null, 0);
                } catch (SQLException e) {
                    writer.clearDSSesBackToMark(writerMark);
                    writeSQLCARDs(e, 0);
                    errorInChain(e);
                }
                break;
            case CodePoint.EXCSAT:
                parseEXCSAT();
                writeEXCSATRD();
                break;
            case CodePoint.ACCSEC:
                int securityCheckCode = parseACCSEC();
                writeACCSECRD(securityCheckCode);

                /* ACCSECRD is the last reply that is mandatorily in EBCDIC */
                if (appRequester.supportsUtf8Ccsid()) {
                    switchToUtf8();
                } else {
                    /* This thread might serve several requests.
                     * Revert if not supported by current client. */
                    switchToEbcdic();
                }
                checkSecurityCodepoint = true;
                break;
            case CodePoint.SECCHK:
                if (parseDRDAConnection())
                    // security all checked and connection ok
                    checkSecurityCodepoint = false;
                break;
            /* since we don't support sqlj, we won't get bind commands from jcc, we
             * might get it from ccc; just skip them.
             */
            case CodePoint.BGNBND:
                reader.skipBytes();
                writeSQLCARDs(null, 0);
                break;
            case CodePoint.BNDSQLSTT:
                reader.skipBytes();
                parseSQLSTTDss();
                writeSQLCARDs(null, 0);
                break;
            case CodePoint.SQLSTTVRB:
                // optional
                reader.skipBytes();
                break;
            case CodePoint.ENDBND:
                reader.skipBytes();
                writeSQLCARDs(null, 0);
                break;
            case CodePoint.DSCSQLSTT:
                if (PRPSQLSTTfailed) {
                    reader.skipBytes();
                    writeSQLCARDs(null, 0);
                    break;
                }
                try {
                    boolean rtnOutput = parseDSCSQLSTT();
                    writeSQLDARD(database.getCurrentStatement(), rtnOutput, null);

                } catch (SQLException e) {
                    writer.clearDSSesBackToMark(writerMark);
                    server.consoleExceptionPrint(e);
                    try {
                        writeSQLDARD(database.getCurrentStatement(), true, e);
                    } catch (SQLException e2) { // should not get here since doing nothing with ps
                        agentError("Why am I getting another SQLException?");
                    }
                    errorInChain(e);
                }
                break;
            case CodePoint.EXCSQLSTT:
                if (PRPSQLSTTfailed) {
                    // Skip parameters too if they are chained Beetle 4867
                    skipRemainder(true);
                    writeSQLCARDs(null, 0);
                    break;
                }
                try {
                    parseEXCSQLSTT();

                    DRDAStatement curStmt = database.getCurrentStatement();
                    if (curStmt != null)
                        curStmt.rsSuspend();
                    writePBSD();
                } catch (SQLException e) {
                    skipRemainder(true);
                    writer.clearDSSesBackToMark(writerMark);
                    if (SanityManager.DEBUG) {
                        server.consoleExceptionPrint(e);
                    }
                    writeSQLCARDs(e, 0);
                    errorInChain(e);
                }
                break;
            case CodePoint.SYNCCTL:
                if (xaProto == null)
                    xaProto = new DRDAXAProtocol(this);
                xaProto.parseSYNCCTL();
                try {
                    writePBSD();
                } catch (SQLException se) {
                    server.consoleExceptionPrint(se);
                    errorInChain(se);
                }
                break;
            default:
                codePointNotSupported(codePoint);
            }

            if (SanityManager.DEBUG) {
                String cpStr = CodePointNameTable.lookup(codePoint);
                try {
                    PiggyBackedSessionData pbsd = database.getPiggyBackedSessionData(false);
                    // DERBY-3596
                    // Don't perform this assert if a deferred reset is
                    // happening or has recently taken place, because the
                    // connection state has been changed under the feet of the
                    // piggy-backing mechanism.
                    if (!this.deferredReset && pbsd != null) {
                        // Session data has already been piggy-backed. Refresh
                        // the data from the connection, to make sure it has
                        // not changed behind our back.
                        pbsd.refresh();
                        SanityManager.ASSERT(!pbsd.isModified(),
                                "Unexpected PBSD modification: " + pbsd + " after codePoint " + cpStr);
                    }
                    // Not having a pbsd here is ok. No data has been
                    // piggy-backed and the client has no cached values.
                    // If needed it will send an explicit request to get
                    // session data
                } catch (SQLException sqle) {
                    server.consoleExceptionPrint(sqle);
                    SanityManager.THROWASSERT("Unexpected exception after " + "codePoint " + cpStr, sqle);
                }
            }

            // Set the correct chaining bits for whatever
            // reply DSS(es) we just wrote.  If we've reached
            // the end of the chain, this method will send
            // the DSS(es) across.
            finalizeChain();

        } while (reader.isChainedWithSameID() || reader.isChainedWithDiffID());
    }

    /**
     * If there's a severe error in the DDM chain, and if the header indicates
     * "terminate chain on error", we stop processing further commands in the chain
     * nor do we send any reply for them.  In accordance to this, a SQLERRRM message 
     * indicating the severe error must have been sent! (otherwise application requestor,
     * such as JCC, would not terminate the receiving of chain replies.)
     *
     * Each DRDA command is processed independently. DRDA defines no interdependencies
     * across chained commands. A command is processed the same when received within
     * a set of chained commands or received separately.  The chaining was originally
     * defined as a way to save network costs.
     *
      * @param e      the SQLException raised
     * @exception   DRDAProtocolException
     */
    private void errorInChain(SQLException e) throws DRDAProtocolException {
        if (reader.terminateChainOnErr() && (getExceptionSeverity(e) > CodePoint.SVRCOD_ERROR)) {
            if (SanityManager.DEBUG)
                trace("terminating the chain on error...");
            skipRemainder(false);
        }
    }

    /**
     * Exchange server attributes with application requester
     *
     * @exception DRDAProtocolException
     */
    private void exchangeServerAttributes() throws DRDAProtocolException {
        int codePoint;
        correlationID = reader.readDssHeader();
        if (SanityManager.DEBUG) {
            if (correlationID == 0) {
                SanityManager.THROWASSERT("Unexpected value for correlationId = " + correlationID);
            }
        }

        codePoint = reader.readLengthAndCodePoint(false);

        // The first code point in the exchange of attributes must be EXCSAT
        if (codePoint != CodePoint.EXCSAT) {
            //Throw PRCCNVRM 
            throw new DRDAProtocolException(DRDAProtocolException.DRDA_Proto_PRCCNVRM, this, codePoint,
                    CodePoint.PRCCNVCD_EXCSAT_FIRST_AFTER_CONN);
        }

        parseEXCSAT();
        writeEXCSATRD();
        finalizeChain();
        session.setState(session.ATTEXC);
    }

    private boolean parseDRDAConnection() throws DRDAProtocolException {
        int codePoint;
        boolean sessionOK = true;

        int securityCheckCode = parseSECCHK();
        if (SanityManager.DEBUG)
            trace("*** SECCHKRM securityCheckCode is: " + securityCheckCode);
        writeSECCHKRM(securityCheckCode);
        //at this point if the security check failed, we're done, the session failed
        if (securityCheckCode != 0) {
            return false;
        }

        correlationID = reader.readDssHeader();
        codePoint = reader.readLengthAndCodePoint(false);
        verifyRequiredObject(codePoint, CodePoint.ACCRDB);
        int svrcod = parseACCRDB();

        //If network server gets a null connection form InternalDriver, reply with
        //RDBAFLRM and SQLCARD with null SQLException 
        if (database.getConnection() == null && databaseAccessException == null) {
            writeRDBfailure(CodePoint.RDBAFLRM);
            return false;
        }

        //if earlier we couldn't access the database
        if (databaseAccessException != null) {

            //if the Database was not found we will try DS
            int failureType = getRdbAccessErrorCodePoint();
            if (failureType == CodePoint.RDBNFNRM || failureType == CodePoint.RDBATHRM) {
                writeRDBfailure(failureType);
            } else {
                writeRDBfailure(CodePoint.RDBAFLRM);
            }
            return false;
        } else if (database.accessCount > 1) // already in conversation with database
        {
            writeRDBfailure(CodePoint.RDBACCRM);
            return false;
        } else // everything is fine 
            writeACCRDBRM(svrcod);

        // compare this application requester with previously stored
        // application requesters and if we have already seen this one
        // use stored application requester 
        session.appRequester = server.getAppRequester(appRequester);
        return sessionOK;
    }

    /**
     * Switch the DDMWriter and DDMReader to UTF8 IF supported
     */
    private void switchToUtf8() {
        writer.setUtf8Ccsid();
        reader.setUtf8Ccsid();
    }

    /**
     * Switch the DDMWriter and DDMReader to EBCDIC
     */
    private void switchToEbcdic() {
        writer.setEbcdicCcsid();
        reader.setEbcdicCcsid();
    }

    /**
     * Write RDB Failure
     *
     * Instance Variables
     *    SVRCOD - Severity Code - required
     *   RDBNAM - Relational Database name - required
     *  SRVDGN - Server Diagnostics - optional (not sent for now)
      *
     * @param   codePoint   codepoint of failure
     */
    private void writeRDBfailure(int codePoint) throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(codePoint);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, CodePoint.SVRCOD_ERROR);
        writeRDBNAM(database.getDatabaseName());
        writer.endDdmAndDss();

        switch (codePoint) {
        case CodePoint.RDBAFLRM:
            //RDBAFLRM requires TYPDEFNAM and TYPDEFOVR
            writer.createDssObject();
            writer.writeScalarString(CodePoint.TYPDEFNAM, CodePoint.TYPDEFNAM_QTDSQLASC);
            writeTYPDEFOVR();
            writer.endDss();
        case CodePoint.RDBNFNRM:
        case CodePoint.RDBATHRM:
            writeSQLCARD(databaseAccessException, CodePoint.SVRCOD_ERROR, 0, 0);
        case CodePoint.RDBACCRM:
            //Ignore anything that was chained to the ACCRDB.
            skipRemainder(false);

            // Finalize chain state for whatever we wrote in
            // response to ACCRDB.
            finalizeChain();
            break;
        }

    }

    /* Check the database access exception and return the appropriate
       error codepoint.
       RDBNFNRM - Database not found
       RDBATHRM - Not Authorized
       RDBAFLRM - Access failure
       @return RDB Access codepoint 
              
    */

    private int getRdbAccessErrorCodePoint() {
        String sqlState = databaseAccessException.getSQLState();
        // These tests are ok since DATABASE_NOT_FOUND and 
        // AUTH_INVALID_USER_NAME are not ambigious error codes (on the first
        // five characters) in SQLState. If they were, we would have to
        // perform a similar check as done in method isAuthenticationException
        if (sqlState.regionMatches(0, SQLState.DATABASE_NOT_FOUND, 0, 5)) {
            // RDB not found codepoint
            return CodePoint.RDBNFNRM;
        } else {
            if (isAuthenticationException(databaseAccessException)
                    || sqlState.regionMatches(0, SQLState.AUTH_INVALID_USER_NAME, 0, 5)) {
                // Not Authorized To RDB reply message codepoint
                return CodePoint.RDBATHRM;
            } else {
                // RDB Access Failed Reply Message codepoint
                return CodePoint.RDBAFLRM;
            }
        }
    }

    /**
     * There are multiple reasons for not getting a connection, and
     * all these should throw SQLExceptions with SQL state 08004
     * according to the SQL standard. Since only one of these SQL
     * states indicate that an authentication error has occurred, it
     * is not enough to check that the SQL state is 08004 and conclude
     * that authentication caused the exception to be thrown.
     *
     * This method tries to cast the exception to an EmbedSQLException
     * and use getMessageId on that object to check for authentication
     * error instead of the SQL state we get from
     * SQLExceptions#getSQLState. getMessageId returns the entire id
     * as defined in SQLState (e.g. 08004.C.1), while getSQLState only
     * return the 5 first characters (i.e. 08004 instead of 08004.C.1)
     *
     * If the cast to EmbedSQLException is not successful, the
     * assumption that SQL State 08004 is caused by an authentication
     * failure is followed even though this is not correct. This was
     * the pre DERBY-3060 way of solving the issue.
     *
     * @param sqlException The exception that is checked to see if
     * this is really caused by an authentication failure
     * @return true if sqlException is (or has to be assumed to be)
     * caused by an authentication failure, false otherwise.
     * @see SQLState
     */
    private boolean isAuthenticationException(SQLException sqlException) {
        boolean authFail = false;

        // get exception which carries Derby messageID and args
        SQLException se = Util.getExceptionFactory().getArgumentFerry(sqlException);

        if (se instanceof EmbedSQLException) {
            // DERBY-3060: if this is an EmbedSQLException, we can
            // check the messageId to find out what caused the
            // exception.

            String msgId = ((EmbedSQLException) se).getMessageId();

            // Of the 08004.C.x messages, only
            // SQLState.NET_CONNECT_AUTH_FAILED is an authentication
            // exception
            if (msgId.equals(SQLState.NET_CONNECT_AUTH_FAILED)) {
                authFail = true;
            }
        } else {
            String sqlState = se.getSQLState();
            if (sqlState.regionMatches(0, SQLState.LOGIN_FAILED, 0, 5)) {
                // Unchanged by DERBY-3060: This is not an
                // EmbedSQLException, so we cannot check the
                // messageId. As before DERBY-3060, we assume that all
                // 08004 error codes are due to an authentication
                // failure, even though this ambigious
                authFail = true;
            }
        }
        return authFail;
    }

    /**
     * Verify userId and password
     *
     * Username and password is verified by making a connection to the
     * database
     *
     * @return security check code, 0 is O.K.
     * @exception DRDAProtocolException
     */
    private int verifyUserIdPassword() throws DRDAProtocolException {
        databaseAccessException = null;
        int retSecChkCode = 0;

        String realName = database.getDatabaseName(); //first strip off properties
        int endOfName = realName.indexOf(';');
        if (endOfName != -1)
            realName = realName.substring(0, endOfName);
        retSecChkCode = getConnFromDatabaseName();
        return retSecChkCode;
    }

    /**
     * Get connection from a database name
     *
     * Username and password is verified by making a connection to the
     * database
     *
     * @return security check code, 0 is O.K.
     * @exception DRDAProtocolException
     */
    private int getConnFromDatabaseName() throws DRDAProtocolException {
        Properties p = new Properties();
        databaseAccessException = null;
        //if we haven't got the correlation token yet, use session number for drdaID
        if (session.drdaID == null)
            session.drdaID = leftBrace + session.connNum + rightBrace;
        p.put(Attribute.DRDAID_ATTR, session.drdaID);

        // We pass extra property information for the authentication provider
        // to successfully re-compute the substitute (hashed) password and
        // compare it with what we've got from the requester (source).
        //
        // If a password attribute appears as part of the connection URL
        // attributes, we then don't use the substitute hashed password
        // to authenticate with the engine _as_ the one (if any) as part
        // of the connection URL attributes, will be used to authenticate
        // against Derby's BUILT-IN authentication provider - As a reminder,
        // Derby allows password to be mentioned as part of the connection
        // URL attributes, as this extra capability could be useful to pass
        // passwords to external authentication providers for Derby; hence
        // a password defined as part of the connection URL attributes cannot
        // be substituted (single-hashed) as it is not recoverable.
        if ((database.securityMechanism == CodePoint.SECMEC_USRSSBPWD)
                && (database.getDatabaseName().indexOf(Attribute.PASSWORD_ATTR) == -1)) {
            p.put(Attribute.DRDA_SECMEC, String.valueOf(database.securityMechanism));
            p.put(Attribute.DRDA_SECTKN_IN,
                    DecryptionManager.toHexString(database.secTokenIn, 0, database.secTokenIn.length));
            p.put(Attribute.DRDA_SECTKN_OUT,
                    DecryptionManager.toHexString(database.secTokenOut, 0, database.secTokenOut.length));
        }

        if (database.securityMechanism == CodePoint.SECMEC_KERSEC) {
            p.put(Attribute.DRDA_KERSEC_AUTHENTICATED, Boolean.toString(gssContext.isEstablished()));
        }

        try {
            database.makeConnection(p);
        } catch (SQLException se) {
            String sqlState = se.getSQLState();

            databaseAccessException = se;
            for (; se != null; se = se.getNextException()) {
                if (SanityManager.DEBUG)
                    trace(se.getMessage());
                println2Log(database.getDatabaseName(), session.drdaID, se.getMessage());
            }

            if (isAuthenticationException(databaseAccessException)) {
                // need to set the security check code based on the
                // reason the connection was denied, Derby doesn't say
                // whether the userid or password caused the problem,
                // so we will just return userid invalid
                return CodePoint.SECCHKCD_USERIDINVALID;
            } else {
                return 0;
            }
        } catch (Exception e) {
            // If Derby has shut down for some reason,
            // we will send  an agent error and then try to 
            // get the driver loaded again.  We have to get
            // rid of the client first in case they are holding
            // the DriverManager lock.
            println2Log(database.getDatabaseName(), session.drdaID, "Driver not loaded" + e.getMessage());
            try {
                agentError("Driver not loaded");
            } catch (DRDAProtocolException dpe) {
                // Retry starting the server before rethrowing 
                // the protocol exception.  Then hopfully all
                // will be well when they try again.
                try {
                    server.startNetworkServer();
                } catch (Exception re) {
                    println2Log(database.getDatabaseName(), session.drdaID,
                            "Failed attempt to reload driver " + re.getMessage());
                }
                throw dpe;
            }
        }

        // Everything worked so log connection to the database.
        if (getLogConnections())
            println2Log(database.getDatabaseName(), session.drdaID,
                    "Apache Derby Network Server connected to database " + database.getDatabaseName());
        return 0;
    }

    /**
     * Parses EXCSAT (Exchange Server Attributes)
     * Instance variables
     *   EXTNAM(External Name)   - optional
     *  MGRLVLLS(Manager Levels) - optional
     *   SPVNAM(Supervisor Name) - optional
     *  SRVCLSNM(Server Class Name) - optional
     *  SRVNAM(Server Name) - optional, ignorable
     *  SRVRLSLV(Server Product Release Level) - optional, ignorable
     *
     * @exception DRDAProtocolException
     */
    private void parseEXCSAT() throws DRDAProtocolException {
        int codePoint;
        String strVal;

        // There are three kinds of EXCSAT's we might get.
        // 1) Initial Exchange attributes.
        //    For this we need to initialize the apprequester.
        //    Session state is set to ATTEXC and then the AR must 
        //    follow up with ACCSEC and SECCHK to get the connection.
        //  2) Send of EXCSAT as ping or mangager level adjustment. 
        //     (see parseEXCSAT2())
        //     For this we just ignore the EXCSAT objects that
        //     are already set.
        //  3) Send of EXCSAT for connection reset. (see parseEXCSAT2())
        //     This is treated just like ping and will be followed up 
        //     by an ACCSEC request if in fact it is a connection reset.

        // If we have already exchanged attributes once just 
        // process any new manager levels and return (case 2 and 3 above)
        this.deferredReset = false; // Always reset, only set to true below.
        if (appRequester != null) {
            // DERBY-3596
            // Don't mess with XA requests, as the logic for these are handled
            // by the server side (embedded) objects. Note that XA requests
            // results in a different database object implementation, and it
            // does not have the bug we are working around.
            if (!appRequester.isXARequester()) {
                this.deferredReset = true; // Non-XA deferred reset detected.
            }
            parseEXCSAT2();
            return;
        }

        // set up a new Application Requester to store information about the
        // application requester for this session

        appRequester = new AppRequester();

        reader.markCollection();

        codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            // optional
            case CodePoint.EXTNAM:
                appRequester.extnam = reader.readString();
                if (SanityManager.DEBUG)
                    trace("extName = " + appRequester.extnam);
                if (appRequester.extnam.length() > CodePoint.MAX_NAME)
                    tooBig(CodePoint.EXTNAM);
                break;
            // optional
            case CodePoint.MGRLVLLS:
                parseMGRLVLLS(1);
                break;
            // optional 
            case CodePoint.SPVNAM:
                appRequester.spvnam = reader.readString();
                // This is specified as a null parameter so length should
                // be zero
                if (appRequester.spvnam != null)
                    badObjectLength(CodePoint.SPVNAM);
                break;
            // optional
            case CodePoint.SRVNAM:
                appRequester.srvnam = reader.readString();
                if (SanityManager.DEBUG)
                    trace("serverName = " + appRequester.srvnam);
                if (appRequester.srvnam.length() > CodePoint.MAX_NAME)
                    tooBig(CodePoint.SRVNAM);
                break;
            // optional
            case CodePoint.SRVRLSLV:
                appRequester.srvrlslv = reader.readString();
                if (SanityManager.DEBUG)
                    trace("serverlslv = " + appRequester.srvrlslv);
                if (appRequester.srvrlslv.length() > CodePoint.MAX_NAME)
                    tooBig(CodePoint.SRVRLSLV);
                break;
            // optional
            case CodePoint.SRVCLSNM:
                appRequester.srvclsnm = reader.readString();
                if (SanityManager.DEBUG)
                    trace("serverClassName = " + appRequester.srvclsnm);
                if (appRequester.srvclsnm.length() > CodePoint.MAX_NAME)
                    tooBig(CodePoint.SRVCLSNM);
                break;
            default:
                invalidCodePoint(codePoint);
            }
            codePoint = reader.getCodePoint();
        }
    }

    /**
     * Parses EXCSAT2 (Exchange Server Attributes)
     * Instance variables
     *   EXTNAM(External Name)   - optional
     *  MGRLVLLS(Manager Levels) - optional
     *   SPVNAM(Supervisor Name) - optional
     *  SRVCLSNM(Server Class Name) - optional
     *  SRVNAM(Server Name) - optional, ignorable
     *  SRVRLSLV(Server Product Release Level) - optional, ignorable
     *
     * @exception DRDAProtocolException
     * 
     * This parses a second occurrence of an EXCSAT command
     * The target must ignore the values for extnam, srvclsnm, srvnam and srvrlslv.
     * I am also going to ignore spvnam since it should be null anyway.
     * Only new managers can be added.
     */
    private void parseEXCSAT2() throws DRDAProtocolException {
        int codePoint;
        reader.markCollection();

        codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            // optional
            case CodePoint.EXTNAM:
            case CodePoint.SRVNAM:
            case CodePoint.SRVRLSLV:
            case CodePoint.SRVCLSNM:
            case CodePoint.SPVNAM:
                reader.skipBytes();
                break;
            // optional
            case CodePoint.MGRLVLLS:
                parseMGRLVLLS(2);
                break;
            default:
                invalidCodePoint(codePoint);
            }
            codePoint = reader.getCodePoint();
        }
    }

    /**
     *   Parse manager levels
     *  Instance variables
     *      MGRLVL - repeatable, required
     *        CODEPOINT
     *         CCSIDMGR - CCSID Manager
     *         CMNAPPC - LU 6.2 Conversational Communications Manager 
     *         CMNSYNCPT - SNA LU 6.2 SyncPoint Conversational Communications Manager
     *         CMNTCPIP - TCP/IP Communication Manager 
     *         DICTIONARY - Dictionary
     *         RDB - Relational Database 
     *         RSYNCMGR - Resynchronization Manager 
     *         SECMGR - Security Manager
     *         SQLAM - SQL Application Manager
     *         SUPERVISOR - Supervisor
     *         SYNCPTMGR - Sync Point Manager 
     *        VALUE
     *
     *   On the second appearance of this codepoint, it can only add managers
     *
     * @param time   1 for first time this is seen, 2 for subsequent ones
     * @exception DRDAProtocolException
     * 
     */
    private void parseMGRLVLLS(int time) throws DRDAProtocolException {
        int manager, managerLevel;
        int currentLevel;
        // set up vectors to keep track of manager information
        unknownManagers = new Vector();
        knownManagers = new Vector();
        errorManagers = new Vector();
        errorManagersLevel = new Vector();
        if (SanityManager.DEBUG)
            trace("Manager Levels");

        while (reader.moreDdmData()) {
            manager = reader.readNetworkShort();
            managerLevel = reader.readNetworkShort();
            if (CodePoint.isKnownManager(manager)) {
                knownManagers.add(new Integer(manager));
                //if the manager level hasn't been set, set it
                currentLevel = appRequester.getManagerLevel(manager);
                if (currentLevel == appRequester.MGR_LEVEL_UNKNOWN)
                    appRequester.setManagerLevel(manager, managerLevel);
                else {
                    //if the level is still the same we'll ignore it
                    if (currentLevel != managerLevel) {
                        //keep a list of conflicting managers
                        errorManagers.add(new Integer(manager));
                        errorManagersLevel.add(new Integer(managerLevel));
                    }
                }

            } else
                unknownManagers.add(new Integer(manager));
            if (SanityManager.DEBUG)
                trace("Manager = " + java.lang.Integer.toHexString(manager) + " ManagerLevel " + managerLevel);
        }
        sqlamLevel = appRequester.getManagerLevel(CodePoint.SQLAM);
        // did we have any errors
        if (errorManagers.size() > 0) {
            Object[] oa = new Object[errorManagers.size() * 2];
            int j = 0;
            for (int i = 0; i < errorManagers.size(); i++) {
                oa[j++] = errorManagers.get(i);
                oa[j++] = errorManagersLevel.get(i);
            }
            throw new DRDAProtocolException(DRDAProtocolException.DRDA_Proto_MGRLVLRM, this, 0, 0, oa);
        }
    }

    /**
     * Write reply to EXCSAT command
     * Instance Variables
     *   EXTNAM - External Name (optional)
     *  MGRLVLLS - Manager Level List (optional)
     *  SRVCLSNM - Server Class Name (optional) - used by JCC
     *  SRVNAM - Server Name (optional)
     *  SRVRLSLV - Server Product Release Level (optional)
     *
     * @exception DRDAProtocolException
     */
    private void writeEXCSATRD() throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.EXCSATRD);
        writer.writeScalarString(CodePoint.EXTNAM, server.att_extnam);
        //only reply with manager levels if we got sent some
        if (knownManagers != null && knownManagers.size() > 0)
            writeMGRLEVELS();
        writer.writeScalarString(CodePoint.SRVCLSNM, server.att_srvclsnm);
        writer.writeScalarString(CodePoint.SRVNAM, server.ATT_SRVNAM);
        writer.writeScalarString(CodePoint.SRVRLSLV, server.att_srvrlslv);
        writer.endDdmAndDss();
    }

    /**
     * Write manager levels
     * The target server must not provide information for any target
     * managers unless the source explicitly requests it.
     * For each manager class, if the target server's support level
      * is greater than or equal to the source server's level, then the source
      * server's level is returned for that class if the target server can operate
      * at the source's level; otherwise a level 0 is returned.  If the target
      * server's support level is less than the source server's level, the
      * target server's level is returned for that class.  If the target server
      * does not recognize the code point of a manager class or does not support
      * that class, it returns a level of 0.  The target server then waits
      * for the next command or for the source server to terminate communications.
      * When the source server receives EXCSATRD, it must compare each of the entries
      * in the mgrlvlls parameter it received to the corresponding entries in the mgrlvlls
      * parameter it sent.  If any level mismatches, the source server must decide
      * whether it can use or adjust to the lower level of target support for that manager
      * class.  There are no architectural criteria for making this decision.
      * The source server can terminate communications or continue at the target
      * servers level of support.  It can also attempt to use whatever
      * commands its user requests while receiving error reply messages for real
      * functional mismatches.
      * The manager levels the source server specifies or the target server
      * returns must be compatible with the manager-level dependencies of the specified
      * manangers.  Incompatible manager levels cannot be specified.
     *  Instance variables
     *      MGRLVL - repeatable, required
     *        CODEPOINT
     *         CCSIDMGR - CCSID Manager
     *         CMNAPPC - LU 6.2 Conversational Communications Manager 
     *         CMNSYNCPT - SNA LU 6.2 SyncPoint Conversational Communications Manager
     *         CMNTCPIP - TCP/IP Communication Manager   
     *         DICTIONARY - Dictionary 
     *         RDB - Relational Database 
     *         RSYNCMGR - Resynchronization Manager   
     *         SECMGR - Security Manager
     *         SQLAM - SQL Application Manager 
     *         SUPERVISOR - Supervisor 
     *         SYNCPTMGR - Sync Point Manager 
     *         XAMGR - XA manager 
     *        VALUE
     */
    private void writeMGRLEVELS() throws DRDAProtocolException {
        int manager;
        int appLevel;
        int serverLevel;
        writer.startDdm(CodePoint.MGRLVLLS);
        for (int i = 0; i < knownManagers.size(); i++) {
            manager = ((Integer) knownManagers.get(i)).intValue();
            appLevel = appRequester.getManagerLevel(manager);
            serverLevel = server.getManagerLevel(manager);
            if (serverLevel >= appLevel) {
                //Note appLevel has already been set to 0 if we can't support
                //the original app Level
                writer.writeCodePoint4Bytes(manager, appLevel);
            } else {
                writer.writeCodePoint4Bytes(manager, serverLevel);
                // reset application manager level to server level
                appRequester.setManagerLevel(manager, serverLevel);
            }
        }
        // write 0 for all unknown managers
        for (int i = 0; i < unknownManagers.size(); i++) {
            manager = ((Integer) unknownManagers.get(i)).intValue();
            writer.writeCodePoint4Bytes(manager, 0);
        }
        writer.endDdm();
    }

    /**
     *  Parse Access Security
     *
     *   If the target server supports the SECMEC requested by the application requester
     *   then a single value is returned and it is identical to the SECMEC value
     *   in the ACCSEC command. If the target server does not support the SECMEC
     *   requested, then one or more values are returned and the application requester
     *  must choose one of these values for the security mechanism.
     *  We currently support
     *      - user id and password (default for JCC)
     *      - encrypted user id and password
      *      - strong password substitute (USRSSBPWD w/
      *                                    Derby network client only)
     *
      *  Instance variables
     *    SECMGRNM  - security manager name - optional
     *     SECMEC    - security mechanism - required
     *     RDBNAM   - relational database name - optional
     *      SECTKN   - security token - optional, (required if sec mech. needs it)
      *
     *  @return security check code - 0 if everything O.K.
     */
    private int parseACCSEC() throws DRDAProtocolException {
        int securityCheckCode = 0;
        int securityMechanism = 0;
        byte[] secTokenIn = null;

        reader.markCollection();
        int codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            //optional
            case CodePoint.SECMGRNM:
                // this is defined to be 0 length
                if (reader.getDdmLength() != 0)
                    badObjectLength(CodePoint.SECMGRNM);
                break;
            //required
            case CodePoint.SECMEC:
                checkLength(CodePoint.SECMEC, 2);
                securityMechanism = reader.readNetworkShort();
                if (SanityManager.DEBUG)
                    trace("parseACCSEC - Security mechanism = " + securityMechanism);

                // if Property.DRDA_PROP_SECURITYMECHANISM has been set, then
                // network server only accepts connections which use that
                // security mechanism. No other types of connections 
                // are accepted.
                // Make check to see if this property has been set.
                // if set, and if the client requested security mechanism 
                // is not the same, then return a security check code 
                // that the server does not support/allow this security 
                // mechanism
                if ((server.getSecurityMechanism() != NetworkServerControlImpl.INVALID_OR_NOTSET_SECURITYMECHANISM)
                        && securityMechanism != server.getSecurityMechanism()) {
                    securityCheckCode = CodePoint.SECCHKCD_NOTSUPPORTED;
                    if (SanityManager.DEBUG) {
                        trace("parseACCSEC - SECCHKCD_NOTSUPPORTED [1] - " + securityMechanism + " <> "
                                + server.getSecurityMechanism() + "\n");
                    }
                } else {
                    // for plain text userid,password USRIDPWD, and USRIDONL
                    // no need of decryptionManager
                    if (securityMechanism != CodePoint.SECMEC_USRIDPWD
                            && securityMechanism != CodePoint.SECMEC_USRIDONL) {
                        if (securityMechanism == CodePoint.SECMEC_KERSEC) {

                            try {
                                user = UserGroupInformation.getCurrentUser();

                                Exception exception = user.doAs(new PrivilegedAction<Exception>() {
                                    @Override
                                    public Exception run() {
                                        try {
                                            // Get own Kerberos credentials for accepting connection
                                            GSSManager manager = GSSManager.getInstance();
                                            Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
                                            GSSCredential serverCreds = manager.createCredential(null,
                                                    GSSCredential.DEFAULT_LIFETIME, krb5Mechanism,
                                                    GSSCredential.ACCEPT_ONLY);

                                            /*
                                             * Create a GSSContext to receive the incoming request
                                             * from the client. Use null for the server credentials
                                             * passed in. This tells the underlying mechanism
                                             * to use whatever credentials it has available that
                                             * can be used to accept this connection.
                                             */
                                            gssContext = manager.createContext((GSSCredential) serverCreds);
                                        } catch (Exception e) {
                                            return e;
                                        }
                                        return null;
                                    }
                                });

                                if (exception != null) {
                                    throw exception;
                                }
                            } catch (Exception e) {
                                println2Log(null, session.drdaID, e.getMessage());
                                // Local security service non-retryable error.
                                securityCheckCode = CodePoint.SECCHKCD_0A;
                            }

                        }
                        // These are the only other mechanisms we understand
                        else if (((securityMechanism != CodePoint.SECMEC_EUSRIDPWD)
                                || (securityMechanism == CodePoint.SECMEC_EUSRIDPWD && !server.supportsEUSRIDPWD()))
                                && (securityMechanism != CodePoint.SECMEC_USRSSBPWD))
                        //securityCheckCode = CodePoint.SECCHKCD_NOTSUPPORTED;
                        {
                            securityCheckCode = CodePoint.SECCHKCD_NOTSUPPORTED;
                            if (SanityManager.DEBUG) {
                                trace("parseACCSEC - SECCHKCD_NOTSUPPORTED [2]\n");
                            }
                        } else {
                            // We delay the initialization and required
                            // processing for SECMEC_USRSSBPWD as we need
                            // to ensure the database is booted so that
                            // we can verify that the current auth scheme
                            // is set to BUILT-IN or NONE. For this we need
                            // to have the RDBNAM codepoint available.
                            //
                            // See validateSecMecUSRSSBPWD() call below
                            if (securityMechanism == CodePoint.SECMEC_USRSSBPWD)
                                break;

                            // SECMEC_EUSRIDPWD initialization
                            try {
                                if (decryptionManager == null)
                                    decryptionManager = new DecryptionManager();
                                myPublicKey = decryptionManager.obtainPublicKey();
                            } catch (SQLException e) {
                                println2Log(null, session.drdaID, e.getMessage());
                                // Local security service non-retryable error.
                                securityCheckCode = CodePoint.SECCHKCD_0A;
                            }
                        }
                    }
                }
                break;
            //optional (currently required for Derby - needed for
            //          DERBY-528 as well)
            case CodePoint.RDBNAM:
                String dbname = parseRDBNAM();
                Database d = session.getDatabase(dbname);
                if (d == null)
                    initializeDatabase(dbname);
                else {
                    // reset database for connection re-use 
                    // DERBY-3596
                    // If we are reusing resources for a new physical
                    // connection, reset the database object. If the client
                    // is in the process of creating a new logical
                    // connection only, don't reset the database object.
                    if (!deferredReset) {
                        d.reset();
                    }
                    database = d;
                }
                break;
            //optional - depending on security Mechanism 
            case CodePoint.SECTKN:
                secTokenIn = reader.readBytes();
                break;
            default:
                invalidCodePoint(codePoint);
            }
            codePoint = reader.getCodePoint();
        }

        // check for required CodePoint's
        if (securityMechanism == 0)
            missingCodePoint(CodePoint.SECMEC);

        if (database == null)
            initializeDatabase(null);
        database.securityMechanism = securityMechanism;
        database.secTokenIn = secTokenIn;

        // If security mechanism is SECMEC_USRSSBPWD, then ensure it can be
        // used for the database or system based on the client's connection
        // URL and its identity.
        if (securityCheckCode == 0 && (database.securityMechanism == CodePoint.SECMEC_USRSSBPWD)) {
            if (SanityManager.DEBUG)
                SanityManager.ASSERT((securityCheckCode == 0), "SECMEC_USRSSBPWD: securityCheckCode should not "
                        + "already be set, found it initialized with " + "a value of '" + securityCheckCode + "'.");
            securityCheckCode = validateSecMecUSRSSBPWD();
        }

        // need security token
        if (securityCheckCode == 0 && (database.securityMechanism == CodePoint.SECMEC_EUSRIDPWD
                || database.securityMechanism == CodePoint.SECMEC_USRSSBPWD) && database.secTokenIn == null)
            securityCheckCode = CodePoint.SECCHKCD_SECTKNMISSING_OR_INVALID;

        // shouldn't have security token
        if (securityCheckCode == 0 && (database.securityMechanism == CodePoint.SECMEC_USRIDPWD
                || database.securityMechanism == CodePoint.SECMEC_USRIDONL) && database.secTokenIn != null)
            securityCheckCode = CodePoint.SECCHKCD_SECTKNMISSING_OR_INVALID;

        if (SanityManager.DEBUG)
            trace("** ACCSECRD securityCheckCode is: " + securityCheckCode);

        // If the security check was successful set the session state to
        // security accesseed.  Otherwise go back to attributes exchanged so we
        // require another ACCSEC
        if (securityCheckCode == 0)
            session.setState(session.SECACC);
        else
            session.setState(session.ATTEXC);

        return securityCheckCode;
    }

    /**
     * Parse OPNQRY
     * Instance Variables
     *  RDBNAM - relational database name - optional
     *  PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
     *  QRYBLKSZ - Query Block Size - required
     *  QRYBLKCTL - Query Block Protocol Control - optional 
     *  MAXBLKEXT - Maximum Number of Extra Blocks - optional - default value 0
     *  OUTOVROPT - Output Override Option
     *  QRYROWSET - Query Rowset Size - optional - level 7
     *  MONITOR - Monitor events - optional.
     *
     * @return RDB Package Name, Consistency Token, and Section Number
     * @exception DRDAProtocolException
     */
    private Pkgnamcsn parseOPNQRY() throws DRDAProtocolException, SQLException {
        Pkgnamcsn pkgnamcsn = null;
        boolean gotQryblksz = false;
        int blksize = 0;
        int qryblkctl = CodePoint.QRYBLKCTL_DEFAULT;
        int maxblkext = CodePoint.MAXBLKEXT_DEFAULT;
        int qryrowset = CodePoint.QRYROWSET_DEFAULT;
        int qryclsimp = DRDAResultSet.QRYCLSIMP_DEFAULT;
        int outovropt = CodePoint.OUTOVRFRS;
        reader.markCollection();
        int codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            //optional
            case CodePoint.RDBNAM:
                setDatabase(CodePoint.OPNQRY);
                break;
            //required
            case CodePoint.PKGNAMCSN:
                pkgnamcsn = parsePKGNAMCSN();
                break;
            //required
            case CodePoint.QRYBLKSZ:
                blksize = parseQRYBLKSZ();
                gotQryblksz = true;
                break;
            //optional
            case CodePoint.QRYBLKCTL:
                qryblkctl = reader.readNetworkShort();
                //The only type of query block control we can specify here
                //is forced fixed row
                if (qryblkctl != CodePoint.FRCFIXROW)
                    invalidCodePoint(qryblkctl);
                if (SanityManager.DEBUG)
                    trace("!!qryblkctl = " + Integer.toHexString(qryblkctl));
                gotQryblksz = true;
                break;
            //optional
            case CodePoint.MAXBLKEXT:
                maxblkext = reader.readSignedNetworkShort();
                if (SanityManager.DEBUG)
                    trace("maxblkext = " + maxblkext);
                break;
            // optional
            case CodePoint.OUTOVROPT:
                outovropt = parseOUTOVROPT();
                break;
            //optional
            case CodePoint.QRYROWSET:
                //Note minimum for OPNQRY is 0
                qryrowset = parseQRYROWSET(0);
                break;
            case CodePoint.QRYCLSIMP:
                // Implicitly close non-scrollable cursor
                qryclsimp = parseQRYCLSIMP();
                break;
            case CodePoint.QRYCLSRLS:
                // Ignore release of read locks.  Nothing we can do here
                parseQRYCLSRLS();
                break;
            // optional
            case CodePoint.MONITOR:
                parseMONITOR();
                break;
            default:
                invalidCodePoint(codePoint);
            }
            codePoint = reader.getCodePoint();
        }
        // check for required variables
        if (pkgnamcsn == null)
            missingCodePoint(CodePoint.PKGNAMCSN);
        if (!gotQryblksz)
            missingCodePoint(CodePoint.QRYBLKSZ);

        // get the statement we are opening
        DRDAStatement stmt = database.getDRDAStatement(pkgnamcsn);
        if (stmt == null) {
            //XXX should really throw a SQL Exception here
            invalidValue(CodePoint.PKGNAMCSN);
        }

        // check that this statement is not already open
        // commenting this check out for now
        // it turns out that JCC doesn't send a close if executeQuery is
        // done again without closing the previous result set
        // this check can't be done since the second executeQuery should work
        //if (stmt.state != DRDAStatement.NOT_OPENED)
        //{
        //   writeQRYPOPRM();
        //   pkgnamcsn = null;
        //}
        //else
        //{
        stmt.setOPNQRYOptions(blksize, qryblkctl, maxblkext, outovropt, qryrowset, qryclsimp);
        //}

        // read the command objects
        // for ps with parameter
        if (reader.isChainedWithSameID()) {
            if (SanityManager.DEBUG)
                trace("&&&&&& parsing SQLDTA");
            parseOPNQRYobjects(stmt);
        }
        return pkgnamcsn;
    }

    /**
     * Parse OPNQRY objects
     * Objects
     *  TYPDEFNAM - Data type definition name - optional
     *  TYPDEFOVR - Type defintion overrides - optional
     *  SQLDTA- SQL Program Variable Data - optional
     *
     * If TYPDEFNAM and TYPDEFOVR are supplied, they apply to the objects
     * sent with the statement.  Once the statement is over, the default values
     * sent in the ACCRDB are once again in effect.  If no values are supplied,
     * the values sent in the ACCRDB are used.
     * Objects may follow in one DSS or in several DSS chained together.
     * 
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void parseOPNQRYobjects(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        int codePoint;
        do {
            correlationID = reader.readDssHeader();
            while (reader.moreDssData()) {
                codePoint = reader.readLengthAndCodePoint(false);
                switch (codePoint) {
                // optional
                case CodePoint.TYPDEFNAM:
                    setStmtOrDbByteOrder(false, stmt, parseTYPDEFNAM());
                    break;
                // optional
                case CodePoint.TYPDEFOVR:
                    parseTYPDEFOVR(stmt);
                    break;
                // optional 
                case CodePoint.SQLDTA:
                    parseSQLDTA(stmt);
                    break;
                // optional
                case CodePoint.EXTDTA:
                    readAndSetAllExtParams(stmt, false);
                    break;
                default:
                    invalidCodePoint(codePoint);
                }
            }
        } while (reader.isChainedWithSameID());

    }

    /**
     * Parse OUTOVROPT - this indicates whether output description can be
     * overridden on just the first CNTQRY or on any CNTQRY
     *
     * @return output override option
     * @exception DRDAProtocolException
     */
    private int parseOUTOVROPT() throws DRDAProtocolException {
        checkLength(CodePoint.OUTOVROPT, 1);
        int outovropt = reader.readUnsignedByte();
        if (SanityManager.DEBUG)
            trace("output override option: " + outovropt);
        if (outovropt != CodePoint.OUTOVRFRS && outovropt != CodePoint.OUTOVRANY)
            invalidValue(CodePoint.OUTOVROPT);
        return outovropt;
    }

    /**
     * Parse QRYBLSZ - this gives the maximum size of the query blocks that
     * can be returned to the requester
     *
     * @return query block size
     * @exception DRDAProtocolException
     */
    private int parseQRYBLKSZ() throws DRDAProtocolException {
        checkLength(CodePoint.QRYBLKSZ, 4);
        int blksize = reader.readNetworkInt();
        if (SanityManager.DEBUG)
            trace("qryblksz = " + blksize);
        if (blksize < CodePoint.QRYBLKSZ_MIN || blksize > CodePoint.QRYBLKSZ_MAX)
            invalidValue(CodePoint.QRYBLKSZ);
        return blksize;
    }

    /**
      * Parse QRYROWSET - this is the number of rows to return
     *
     * @param minVal - minimum value
     * @return query row set size
     * @exception DRDAProtocolException
     */
    private int parseQRYROWSET(int minVal) throws DRDAProtocolException {
        checkLength(CodePoint.QRYROWSET, 4);
        int qryrowset = reader.readNetworkInt();
        if (SanityManager.DEBUG)
            trace("qryrowset = " + qryrowset);
        if (qryrowset < minVal || qryrowset > CodePoint.QRYROWSET_MAX)
            invalidValue(CodePoint.QRYROWSET);
        return qryrowset;
    }

    /** Parse a QRYCLSIMP - Implicitly close non-scrollable cursor 
     * after end of data.
     * @return  true to close on end of data 
     */
    private int parseQRYCLSIMP() throws DRDAProtocolException {

        checkLength(CodePoint.QRYCLSIMP, 1);
        int qryclsimp = reader.readUnsignedByte();
        if (SanityManager.DEBUG)
            trace("qryclsimp = " + qryclsimp);
        if (qryclsimp != CodePoint.QRYCLSIMP_SERVER_CHOICE && qryclsimp != CodePoint.QRYCLSIMP_YES
                && qryclsimp != CodePoint.QRYCLSIMP_NO)
            invalidValue(CodePoint.QRYCLSIMP);
        return qryclsimp;
    }

    private int parseQRYCLSRLS() throws DRDAProtocolException {
        reader.skipBytes();
        return 0;
    }

    /**
     * Write a QRYPOPRM - Query Previously opened
     * Instance Variables
     *  SVRCOD - Severity Code - required - 8 ERROR
     *  RDBNAM - Relational Database Name - required
     *  PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number - required
     * 
     * @exception DRDAProtocolException
     */
    private void writeQRYPOPRM() throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.QRYPOPRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, CodePoint.SVRCOD_ERROR);
        writeRDBNAM(database.getDatabaseName());
        writePKGNAMCSN();
        writer.endDdmAndDss();
    }

    /**
     * Write a QRYNOPRM - Query Not Opened
     * Instance Variables
     *  SVRCOD - Severity Code - required -  4 Warning 8 ERROR
     *  RDBNAM - Relational Database Name - required
     *  PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number - required
     * 
     * @param svrCod   Severity Code
     * @exception DRDAProtocolException
     */
    private void writeQRYNOPRM(int svrCod) throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.QRYNOPRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, svrCod);
        writeRDBNAM(database.getDatabaseName());
        writePKGNAMCSN();
        writer.endDdmAndDss();
    }

    /**
     * Write a OPNQFLRM - Open Query Failure
     * Instance Variables
     *  SVRCOD - Severity Code - required - 8 ERROR
     *  RDBNAM - Relational Database Name - required
     *
     * @param   e   Exception describing failure
     *
     * @exception DRDAProtocolException
     */
    private void writeOPNQFLRM(SQLException e) throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.OPNQFLRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, CodePoint.SVRCOD_ERROR);
        writeRDBNAM(database.getDatabaseName());
        writer.endDdm();
        writer.startDdm(CodePoint.SQLCARD);
        writeSQLCAGRP(e, 0, 0);
        writer.endDdmAndDss();
    }

    /**
     * Write PKGNAMCSN
     * Instance Variables
     *   NAMESYMDR - database name - not validated
     *   RDBCOLID - RDB Collection Identifier
     *   PKGID - RDB Package Identifier
     *   PKGCNSTKN - RDB Package Consistency Token
     *   PKGSN - RDB Package Section Number
     *
     * There are two possible formats, fixed and extended which includes length
     * information for the strings
     *
     * @throws DRDAProtocolException
     */
    private void writePKGNAMCSN(byte[] pkgcnstkn) throws DRDAProtocolException {
        writer.startDdm(CodePoint.PKGNAMCSN);
        if (rdbnam.length() <= CodePoint.RDBNAM_LEN && rdbcolid.length() <= CodePoint.RDBCOLID_LEN
                && pkgid.length() <= CodePoint.PKGID_LEN) { // if none of RDBNAM, RDBCOLID and PKGID have a length of
                                                                                                                                                       // more than 18, use fixed format
            writer.writeScalarPaddedString(rdbnam, CodePoint.RDBNAM_LEN);
            writer.writeScalarPaddedString(rdbcolid, CodePoint.RDBCOLID_LEN);
            writer.writeScalarPaddedString(pkgid, CodePoint.PKGID_LEN);
            writer.writeScalarPaddedBytes(pkgcnstkn, CodePoint.PKGCNSTKN_LEN, (byte) 0);
            writer.writeShort(pkgsn);
        } else // extended format
        {
            int len = Math.max(CodePoint.RDBNAM_LEN, rdbnam.length());
            writer.writeShort(len);
            writer.writeScalarPaddedString(rdbnam, len);
            len = Math.max(CodePoint.RDBCOLID_LEN, rdbcolid.length());
            writer.writeShort(len);
            writer.writeScalarPaddedString(rdbcolid, len);
            len = Math.max(CodePoint.PKGID_LEN, pkgid.length());
            writer.writeShort(len);
            writer.writeScalarPaddedString(pkgid, len);
            writer.writeScalarPaddedBytes(pkgcnstkn, CodePoint.PKGCNSTKN_LEN, (byte) 0);
            writer.writeShort(pkgsn);
        }
        writer.endDdm();
    }

    private void writePKGNAMCSN() throws DRDAProtocolException {
        writePKGNAMCSN(pkgcnstkn.getBytes());
    }

    /**
     * Parse CNTQRY - Continue Query
     * Instance Variables
     *   RDBNAM - Relational Database Name - optional
     *   PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number - required
     *   QRYBLKSZ - Query Block Size - required
     *   QRYRELSCR - Query Relative Scrolling Action - optional
     *   QRYSCRORN - Query Scroll Orientation - optional - level 7
     *   QRYROWNBR - Query Row Number - optional
     *   QRYROWSNS - Query Row Sensitivity - optional - level 7
     *   QRYBLKRST - Query Block Reset - optional - level 7
     *   QRYRTNDTA - Query Returns Data - optional - level 7
     *   QRYROWSET - Query Rowset Size - optional - level 7
     *   QRYRFRTBL - Query Refresh Answer Set Table - optional
     *   NBRROW - Number of Fetch or Insert Rows - optional
     *   MAXBLKEXT - Maximum number of extra blocks - optional
     *   RTNEXTDTA - Return of EXTDTA Option - optional
     *   MONITOR - Monitor events - optional.
     *
     * @return DRDAStatement we are continuing
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private DRDAStatement parseCNTQRY() throws DRDAProtocolException, SQLException {
        byte val;
        Pkgnamcsn pkgnamcsn = null;
        boolean gotQryblksz = false;
        boolean qryrelscr = true;
        long qryrownbr = 1;
        boolean qryrfrtbl = false;
        int nbrrow = 1;
        int blksize = 0;
        int maxblkext = -1;
        long qryinsid;
        boolean gotQryinsid = false;
        int qryscrorn = CodePoint.QRYSCRREL;
        boolean qryrowsns = false;
        boolean gotQryrowsns = false;
        boolean qryblkrst = false;
        boolean qryrtndta = true;
        int qryrowset = CodePoint.QRYROWSET_DEFAULT;
        int rtnextdta = CodePoint.RTNEXTROW;
        reader.markCollection();
        int codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            //optional
            case CodePoint.RDBNAM:
                setDatabase(CodePoint.CNTQRY);
                break;
            //required
            case CodePoint.PKGNAMCSN:
                pkgnamcsn = parsePKGNAMCSN();
                break;
            //required
            case CodePoint.QRYBLKSZ:
                blksize = parseQRYBLKSZ();
                gotQryblksz = true;
                break;
            //optional
            case CodePoint.QRYRELSCR:
                qryrelscr = readBoolean(CodePoint.QRYRELSCR);
                if (SanityManager.DEBUG)
                    trace("qryrelscr = " + qryrelscr);
                break;
            //optional
            case CodePoint.QRYSCRORN:
                checkLength(CodePoint.QRYSCRORN, 1);
                qryscrorn = reader.readUnsignedByte();
                if (SanityManager.DEBUG)
                    trace("qryscrorn = " + qryscrorn);
                switch (qryscrorn) {
                case CodePoint.QRYSCRREL:
                case CodePoint.QRYSCRABS:
                case CodePoint.QRYSCRAFT:
                case CodePoint.QRYSCRBEF:
                    break;
                default:
                    invalidValue(CodePoint.QRYSCRORN);
                }
                break;
            //optional
            case CodePoint.QRYROWNBR:
                checkLength(CodePoint.QRYROWNBR, 8);
                qryrownbr = reader.readNetworkLong();
                if (SanityManager.DEBUG)
                    trace("qryrownbr = " + qryrownbr);
                break;
            //optional
            case CodePoint.QRYROWSNS:
                checkLength(CodePoint.QRYROWSNS, 1);
                qryrowsns = readBoolean(CodePoint.QRYROWSNS);
                if (SanityManager.DEBUG)
                    trace("qryrowsns = " + qryrowsns);
                gotQryrowsns = true;
                break;
            //optional
            case CodePoint.QRYBLKRST:
                checkLength(CodePoint.QRYBLKRST, 1);
                qryblkrst = readBoolean(CodePoint.QRYBLKRST);
                if (SanityManager.DEBUG)
                    trace("qryblkrst = " + qryblkrst);
                break;
            //optional
            case CodePoint.QRYRTNDTA:
                qryrtndta = readBoolean(CodePoint.QRYRTNDTA);
                if (SanityManager.DEBUG)
                    trace("qryrtndta = " + qryrtndta);
                break;
            //optional
            case CodePoint.QRYROWSET:
                //Note minimum for CNTQRY is 1
                qryrowset = parseQRYROWSET(1);
                if (SanityManager.DEBUG)
                    trace("qryrowset = " + qryrowset);
                break;
            //optional
            case CodePoint.QRYRFRTBL:
                qryrfrtbl = readBoolean(CodePoint.QRYRFRTBL);
                if (SanityManager.DEBUG)
                    trace("qryrfrtbl = " + qryrfrtbl);
                break;
            //optional
            case CodePoint.NBRROW:
                checkLength(CodePoint.NBRROW, 4);
                nbrrow = reader.readNetworkInt();
                if (SanityManager.DEBUG)
                    trace("nbrrow = " + nbrrow);
                break;
            //optional
            case CodePoint.MAXBLKEXT:
                checkLength(CodePoint.MAXBLKEXT, 2);
                maxblkext = reader.readSignedNetworkShort();
                if (SanityManager.DEBUG)
                    trace("maxblkext = " + maxblkext);
                break;
            //optional
            case CodePoint.RTNEXTDTA:
                checkLength(CodePoint.RTNEXTDTA, 1);
                rtnextdta = reader.readUnsignedByte();
                if (rtnextdta != CodePoint.RTNEXTROW && rtnextdta != CodePoint.RTNEXTALL)
                    invalidValue(CodePoint.RTNEXTDTA);
                if (SanityManager.DEBUG)
                    trace("rtnextdta = " + rtnextdta);
                break;
            // required for SQLAM >= 7
            case CodePoint.QRYINSID:
                checkLength(CodePoint.QRYINSID, 8);
                qryinsid = reader.readNetworkLong();
                gotQryinsid = true;
                if (SanityManager.DEBUG)
                    trace("qryinsid = " + qryinsid);
                break;
            // optional
            case CodePoint.MONITOR:
                parseMONITOR();
                break;
            default:
                invalidCodePoint(codePoint);
            }
            codePoint = reader.getCodePoint();
        }
        // check for required variables
        if (pkgnamcsn == null)
            missingCodePoint(CodePoint.PKGNAMCSN);
        if (!gotQryblksz)
            missingCodePoint(CodePoint.QRYBLKSZ);
        if (sqlamLevel >= MGRLVL_7 && !gotQryinsid)
            missingCodePoint(CodePoint.QRYINSID);

        // get the statement we are continuing
        DRDAStatement stmt = database.getDRDAStatement(pkgnamcsn);
        if (stmt == null) {
            //XXX should really throw a SQL Exception here
            invalidValue(CodePoint.CNTQRY);
        }

        if (stmt.rsIsClosed()) {
            writeQRYNOPRM(CodePoint.SVRCOD_ERROR);
            skipRemainder(true);
            return null;
        }
        stmt.setQueryOptions(blksize, qryrelscr, qryrownbr, qryrfrtbl, nbrrow, maxblkext, qryscrorn, qryrowsns,
                qryblkrst, qryrtndta, qryrowset, rtnextdta);

        if (reader.isChainedWithSameID())
            parseCNTQRYobjects(stmt);
        return stmt;
    }

    /**
     * Skip remainder of current DSS and all chained DSS'es
     *
     * @param onlySkipSameIds True if we _only_ want to skip DSS'es
     *   that are chained with the SAME id as the current DSS.
     *   False means skip ALL chained DSSes, whether they're
     *   chained with same or different ids.
     * @exception DRDAProtocolException
     */
    private void skipRemainder(boolean onlySkipSameIds) throws DRDAProtocolException {
        reader.skipDss();
        while (reader.isChainedWithSameID() || (!onlySkipSameIds && reader.isChainedWithDiffID())) {
            reader.readDssHeader();
            reader.skipDss();
        }
    }

    /**
     * Parse CNTQRY objects
     * Instance Variables
     *   OUTOVR - Output Override Descriptor - optional
     *
     * @param stmt DRDA statement we are working on
     * @exception DRDAProtocolException
     */
    private void parseCNTQRYobjects(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        int codePoint;
        do {
            correlationID = reader.readDssHeader();
            while (reader.moreDssData()) {
                codePoint = reader.readLengthAndCodePoint(false);
                switch (codePoint) {
                // optional
                case CodePoint.OUTOVR:
                    parseOUTOVR(stmt);
                    break;
                default:
                    invalidCodePoint(codePoint);
                }
            }
        } while (reader.isChainedWithSameID());

    }

    /**
     * Parse OUTOVR - Output Override Descriptor
     * This specifies the output format for data to be returned as output to a SQL
     * statement or as output from a query.
     *
     * @param stmt   DRDA statement this applies to
     * @exception DRDAProtocolException
     */
    private void parseOUTOVR(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        boolean first = true;
        int numVars;
        int dtaGrpLen;
        int tripType;
        int tripId;
        int precision;
        int start = 0;
        while (true) {
            dtaGrpLen = reader.readUnsignedByte();
            tripType = reader.readUnsignedByte();
            tripId = reader.readUnsignedByte();
            // check if we have reached the end of the data
            if (tripType == FdocaConstants.RLO_TRIPLET_TYPE) {
                //read last part of footer
                reader.skipBytes();
                break;
            }
            numVars = (dtaGrpLen - 3) / 3;
            if (SanityManager.DEBUG)
                trace("num of vars is: " + numVars);
            int[] outovr_drdaType = null;
            if (first) {
                outovr_drdaType = new int[numVars];
                first = false;
            } else {
                int[] oldoutovr_drdaType = stmt.getOutovr_drdaType();
                int oldlen = oldoutovr_drdaType.length;
                // create new array and copy over already read stuff
                outovr_drdaType = new int[oldlen + numVars];
                System.arraycopy(oldoutovr_drdaType, 0, outovr_drdaType, 0, oldlen);
                start = oldlen;
            }
            for (int i = start; i < numVars + start; i++) {
                int drdaType = reader.readUnsignedByte();
                if (!database.supportsLocator()) {
                    // ignore requests for locator when it is not supported
                    if ((drdaType >= DRDAConstants.DRDA_TYPE_LOBLOC)
                            && (drdaType <= DRDAConstants.DRDA_TYPE_NCLOBLOC)) {
                        if (SanityManager.DEBUG) {
                            trace("ignoring drdaType: " + drdaType);
                        }
                        reader.readNetworkShort(); // Skip rest
                        continue;
                    }
                }
                outovr_drdaType[i] = drdaType;
                if (SanityManager.DEBUG)
                    trace("drdaType is: " + outovr_drdaType[i]);
                precision = reader.readNetworkShort();
                if (SanityManager.DEBUG)
                    trace("drdaLength is: " + precision);
                outovr_drdaType[i] |= (precision << 8);
            }
            stmt.setOutovr_drdaType(outovr_drdaType);
        }
    }

    /**
     * Piggy-back any modified session attributes on the current message. Writes
     * a PBSD conataining one or both of PBSD_ISO and PBSD_SCHEMA. PBSD_ISO is
     * followed by the jdbc isolation level as an unsigned byte. PBSD_SCHEMA is
     * followed by the name of the current schema as an UTF-8 String.
     * @throws java.sql.SQLException
     * @throws com.splicemachine.db.impl.drda.DRDAProtocolException
     */
    private void writePBSD() throws SQLException, DRDAProtocolException {
        if (!appRequester.supportsSessionDataCaching()) {
            return;
        }
        PiggyBackedSessionData pbsd = database.getPiggyBackedSessionData(true);
        if (SanityManager.DEBUG) {
            SanityManager.ASSERT(pbsd != null, "pbsd is not expected to be null");
        }
        // DERBY-3596
        // Reset the flag. In sane builds it is used to avoid an assert, but
        // we want to reset it as soon as possible to avoid masking real bugs.
        // We have to do this because we are changing the connection state
        // at an unexpected time (deferred reset, see parseSECCHK). This was
        // done to avoid having to change the client code.
        this.deferredReset = false;
        pbsd.refresh();
        if (pbsd.isModified()) {
            writer.createDssReply();
            writer.startDdm(CodePoint.PBSD);

            if (pbsd.isIsoModified()) {
                writer.writeScalar1Byte(CodePoint.PBSD_ISO, pbsd.getIso());
            }

            if (pbsd.isSchemaModified()) {
                writer.startDdm(CodePoint.PBSD_SCHEMA);
                writer.writeString(pbsd.getSchema());
                writer.endDdm();
            }
            writer.endDdmAndDss();
        }
        pbsd.setUnmodified();
        if (SanityManager.DEBUG) {
            PiggyBackedSessionData pbsdNew = database.getPiggyBackedSessionData(true);
            SanityManager.ASSERT(pbsdNew == pbsd,
                    "pbsdNew and pbsd are expected to reference " + "the same object");
            pbsd.refresh();
            SanityManager.ASSERT(!pbsd.isModified(), "pbsd=(" + pbsd + ") is not expected to be modified");
        }
    }

    /**
     * Write OPNQRYRM - Open Query Complete
     * Instance Variables
     *   SVRCOD - Severity Code - required
     *   QRYPRCTYP - Query Protocol Type - required
     *   SQLCSRHLD - Hold Cursor Position - optional
     *   QRYATTSCR - Query Attribute for Scrollability - optional - level 7
     *   QRYATTSNS - Query Attribute for Sensitivity - optional - level 7
     *   QRYATTUPD - Query Attribute for Updatability -optional - level 7
     *   QRYINSID - Query Instance Identifier - required - level 7
     *   SRVDGN - Server Diagnostic Information - optional
     *
     * @param isDssObject - return as a DSS object (part of a reply) 
     * @param stmt - DRDA statement we are processing
     *
     * @exception DRDAProtocolException
     */
    private void writeOPNQRYRM(boolean isDssObject, DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        if (SanityManager.DEBUG)
            trace("WriteOPNQRYRM");

        if (isDssObject)
            writer.createDssObject();
        else
            writer.createDssReply();
        writer.startDdm(CodePoint.OPNQRYRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, CodePoint.SVRCOD_INFO);

        // There is currently a problem specifying LMTBLKPRC for LOBs with JCC
        // JCC will throw an ArrayOutOfBounds exception.  Once this is fixed, we
        // don't need to pass the two arguments for getQryprctyp.
        int prcType = stmt.getQryprctyp();
        if (SanityManager.DEBUG)
            trace("sending QRYPRCTYP: " + prcType);
        writer.writeScalar2Bytes(CodePoint.QRYPRCTYP, prcType);

        //pass the SQLCSRHLD codepoint only if statement producing the ResultSet has 
        //hold cursors over commit set. In case of stored procedures which use server-side
        //JDBC, the holdability of the ResultSet will be the holdability of the statement 
        //in the stored procedure, not the holdability of the calling statement.
        if (stmt.getCurrentDrdaResultSet().withHoldCursor == ResultSet.HOLD_CURSORS_OVER_COMMIT)
            writer.writeScalar1Byte(CodePoint.SQLCSRHLD, CodePoint.TRUE);
        if (sqlamLevel >= MGRLVL_7) {
            writer.writeScalarHeader(CodePoint.QRYINSID, 8);
            //This is implementer defined.  DB2 uses this for the nesting level
            //of the query.  A query from an application would be nesting level 0,
            //from a stored procedure, nesting level 1, from a recursive call of
            //a stored procedure, nesting level 2, etc.
            writer.writeInt(0);
            //This is a unique sequence number per session
            writer.writeInt(session.qryinsid++);
            //Write the scroll attributes if they are set
            if (stmt.isScrollable()) {
                writer.writeScalar1Byte(CodePoint.QRYATTSCR, CodePoint.TRUE);
                if ((stmt.getConcurType() == ResultSet.CONCUR_UPDATABLE)
                        && (stmt.getResultSet().getType() == ResultSet.TYPE_SCROLL_INSENSITIVE)) {
                    writer.writeScalar1Byte(CodePoint.QRYATTSNS, CodePoint.QRYSNSSTC);
                } else {
                    writer.writeScalar1Byte(CodePoint.QRYATTSNS, CodePoint.QRYINS);
                }
            }
            if (stmt.getConcurType() == ResultSet.CONCUR_UPDATABLE) {
                if (stmt.getResultSet() != null) {
                    // Resultset concurrency can be less than statement
                    // concurreny if the underlying language resultset
                    // is not updatable.
                    if (stmt.getResultSet().getConcurrency() == ResultSet.CONCUR_UPDATABLE) {
                        writer.writeScalar1Byte(CodePoint.QRYATTUPD, CodePoint.QRYUPD);
                    } else {
                        writer.writeScalar1Byte(CodePoint.QRYATTUPD, CodePoint.QRYRDO);
                    }
                } else {
                    writer.writeScalar1Byte(CodePoint.QRYATTUPD, CodePoint.QRYUPD);
                }
            } else {
                writer.writeScalar1Byte(CodePoint.QRYATTUPD, CodePoint.QRYRDO);
            }
        }
        writer.endDdmAndDss();
    }

    /**
     * Write ENDQRYRM - query process has terminated in such a manner that the
     *   query or result set is now closed.  It cannot be resumed with the CNTQRY
     *  command or closed with the CLSQRY command
     * @param svrCod  Severity code - WARNING or ERROR
     * @exception DRDAProtocolException
     */
    private void writeENDQRYRM(int svrCod) throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.ENDQRYRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, svrCod);
        writer.endDdmAndDss();
    }

    /**
        * Write ABNUOWRM - query process has terminated in an error condition
        * such as deadlock or lock timeout.
        * Severity code is always error
        *     * @exception DRDAProtocolException
        */
    private void writeABNUOWRM() throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.ABNUOWRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, CodePoint.SVRCOD_ERROR);
        writeRDBNAM(database.getDatabaseName());
        writer.endDdmAndDss();
    }

    /**
     * Parse database name
     *
     * @return database name
     *
     * @exception DRDAProtocolException
     */
    private String parseRDBNAM() throws DRDAProtocolException {
        String name;
        byte[] rdbName = reader.readBytes();
        if (rdbName.length == 0) {
            // throw RDBNFNRM
            rdbNotFound(null);
        }
        //SQLAM level 7 allows db name up to 255, level 6 fixed len 18
        if (rdbName.length < CodePoint.RDBNAM_LEN || rdbName.length > CodePoint.MAX_NAME)
            badObjectLength(CodePoint.RDBNAM);
        name = reader.convertBytes(rdbName);
        // trim trailing blanks from the database name
        name = name.trim();
        if (SanityManager.DEBUG)
            trace("RdbName " + name);
        return name;
    }

    /**
     * Write ACCSECRD
     * If the security mechanism is known, we just send it back along with
     * the security token if encryption is going to be used.
     * If the security mechanism is not known, we send a list of the ones
     * we know.
     * Instance Variables
     *    SECMEC - security mechanism - required
     *   SECTKN - security token   - optional (required if security mechanism 
     *                  uses encryption)
     *  SECCHKCD - security check code - error occurred in processing ACCSEC
     *
     * @param securityCheckCode
     * 
     * @exception DRDAProtocolException
     */
    private void writeACCSECRD(int securityCheckCode) throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.ACCSECRD);

        if (securityCheckCode != CodePoint.SECCHKCD_NOTSUPPORTED)
            writer.writeScalar2Bytes(CodePoint.SECMEC, database.securityMechanism);
        else {
            // if server doesnt recognize or allow the client requested security mechanism,
            // then need to return the list of security mechanisms supported/allowed by the server

            // check if server is set to accept connections from client at a certain 
            // security mechanism, if so send only the security mechanism  that the 
            // server will accept, to the client
            if (server.getSecurityMechanism() != NetworkServerControlImpl.INVALID_OR_NOTSET_SECURITYMECHANISM)
                writer.writeScalar2Bytes(CodePoint.SECMEC, server.getSecurityMechanism());
            else {
                // note: per the DDM manual , ACCSECRD response is of 
                // form SECMEC (value{value..})  
                // Need to fix the below to send a list of supported security 
                // mechanisms for value of one SECMEC codepoint (JIRA 926)
                // these are the ones we know about
                writer.writeScalar2Bytes(CodePoint.SECMEC, CodePoint.SECMEC_USRIDPWD);
                // include EUSRIDPWD in the list of supported secmec only if 
                // server can truely support it in the jvm that is running in
                if (server.supportsEUSRIDPWD())
                    writer.writeScalar2Bytes(CodePoint.SECMEC, CodePoint.SECMEC_EUSRIDPWD);
                writer.writeScalar2Bytes(CodePoint.SECMEC, CodePoint.SECMEC_USRIDONL);
                writer.writeScalar2Bytes(CodePoint.SECMEC, CodePoint.SECMEC_USRSSBPWD);
            }
        }

        if (securityCheckCode != 0) {
            writer.writeScalar1Byte(CodePoint.SECCHKCD, securityCheckCode);
        } else {
            // we need to send back the key if encryption is being used
            if (database.securityMechanism == CodePoint.SECMEC_EUSRIDPWD)
                writer.writeScalarBytes(CodePoint.SECTKN, myPublicKey);
            else if (database.securityMechanism == CodePoint.SECMEC_USRSSBPWD)
                writer.writeScalarBytes(CodePoint.SECTKN, myTargetSeed);
        }

        if (database.securityMechanism == CodePoint.SECMEC_KERSEC) {
            writer.endDdm();

            writer.startDdm(CodePoint.KERSECPPL);
            writer.writeString(user.getUserName());
        }
        writer.endDdmAndDss();

        if (securityCheckCode != 0) {
            // then we have an error and so can ignore the rest of the
            // DSS request chain.
            skipRemainder(false);
        }

        finalizeChain();
    }

    /**
     * Parse security check
     * Instance Variables
     *   SECMGRNM - security manager name - optional, ignorable
     *  SECMEC   - security mechanism - required
     *   SECTKN   - security token - optional, (required if encryption used)
     *   PASSWORD - password - optional, (required if security mechanism uses it)
     *   NEWPASSWORD - new password - optional, (required if sec mech. uses it)
     *   USRID   - user id - optional, (required if sec mec. uses it)
     *   RDBNAM   - database name - optional (required if databases can have own sec.)
     *
     * 
     * @return security check code
     * @exception DRDAProtocolException
     */
    private int parseSECCHK() throws DRDAProtocolException {
        int codePoint, securityCheckCode = 0;
        int securityMechanism = 0;
        databaseAccessException = null;
        reader.markCollection();
        codePoint = reader.getCodePoint();
        if (this.deferredReset) {
            // Skip the SECCHK, but assure a minimal degree of correctness.
            while (codePoint != -1) {
                switch (codePoint) {
                // Note the fall-through.
                // Minimal level of checking to detect protocol errors.
                // NOTE: SECMGR level 8 code points are not handled.
                case CodePoint.SECMGRNM:
                case CodePoint.SECMEC:
                case CodePoint.SECTKN:
                case CodePoint.PASSWORD:
                case CodePoint.NEWPASSWORD:
                case CodePoint.USRID:
                case CodePoint.RDBNAM:
                    reader.skipBytes();
                    break;
                default:
                    invalidCodePoint(codePoint);
                }
                codePoint = reader.getCodePoint();
            }
        } else {
            while (codePoint != -1) {
                switch (codePoint) {
                //optional, ignorable
                case CodePoint.SECMGRNM:
                    reader.skipBytes();
                    break;
                //required
                case CodePoint.SECMEC:
                    checkLength(CodePoint.SECMEC, 2);
                    securityMechanism = reader.readNetworkShort();
                    if (SanityManager.DEBUG)
                        trace("parseSECCHK - Security mechanism = " + securityMechanism);
                    //RESOLVE - spec is not clear on what should happen
                    //in this case
                    if (securityMechanism != database.securityMechanism)
                        invalidValue(CodePoint.SECMEC);
                    break;
                //optional - depending on security Mechanism 
                case CodePoint.SECTKN:
                    if ((database.securityMechanism != CodePoint.SECMEC_EUSRIDPWD)
                            && (database.securityMechanism != CodePoint.SECMEC_USRSSBPWD)
                            && (database.securityMechanism != CodePoint.SECMEC_KERSEC))

                    {
                        securityCheckCode = CodePoint.SECCHKCD_SECTKNMISSING_OR_INVALID;
                        reader.skipBytes();
                    } else if (database.securityMechanism == CodePoint.SECMEC_EUSRIDPWD) {
                        if (database.decryptedUserId == null) {
                            try {
                                database.decryptedUserId = reader.readEncryptedString(decryptionManager,
                                        database.securityMechanism, myPublicKey, database.secTokenIn);
                            } catch (SQLException se) {
                                println2Log(database.getDatabaseName(), session.drdaID, se.getMessage());
                                if (securityCheckCode == 0)
                                    //userid invalid
                                    securityCheckCode = CodePoint.SECCHKCD_13;
                            }
                            database.userId = database.decryptedUserId;
                            if (SanityManager.DEBUG)
                                trace("**decrypted userid is: " + database.userId);
                        } else if (database.decryptedPassword == null) {
                            try {
                                database.decryptedPassword = reader.readEncryptedString(decryptionManager,
                                        database.securityMechanism, myPublicKey, database.secTokenIn);
                            } catch (SQLException se) {
                                println2Log(database.getDatabaseName(), session.drdaID, se.getMessage());
                                if (securityCheckCode == 0)
                                    //password invalid
                                    securityCheckCode = CodePoint.SECCHKCD_0F;
                            }
                            database.password = database.decryptedPassword;
                            if (SanityManager.DEBUG)
                                trace("**decrypted password is: " + database.password);
                        }
                    } else if (database.securityMechanism == CodePoint.SECMEC_USRSSBPWD) {
                        if (database.passwordSubstitute == null) {
                            database.passwordSubstitute = reader.readBytes();
                            if (SanityManager.DEBUG)
                                trace("** Substitute Password is:" + DecryptionManager.toHexString(
                                        database.passwordSubstitute, 0, database.passwordSubstitute.length));
                            database.password = DecryptionManager.toHexString(database.passwordSubstitute, 0,
                                    database.passwordSubstitute.length);
                        }
                    } else if (database.securityMechanism == CodePoint.SECMEC_KERSEC) {
                        try {
                            final byte[] secToken = reader.readBytes();
                            Exception exception = user.doAs(new PrivilegedAction<Exception>() {
                                @Override
                                public Exception run() {
                                    try {
                                        database.secTokenOut = gssContext.acceptSecContext(secToken, 0,
                                                secToken.length);
                                    } catch (GSSException e) {
                                        return e;
                                    }
                                    return null;
                                }
                            });
                            if (exception != null)
                                throw exception;

                            if (!gssContext.isEstablished()) {
                                securityCheckCode = CodePoint.SECCHKCD_CONTINUE;
                            } else {
                                securityCheckCode = CodePoint.SECCHKCD_OK;
                            }
                        } catch (Exception e) {
                            handleException(e);
                        }
                    } else {
                        tooMany(CodePoint.SECTKN);
                    }
                    break;
                //optional - depending on security Mechanism
                case CodePoint.PASSWORD:
                    database.password = reader.readString();
                    if (SanityManager.DEBUG)
                        trace("PASSWORD " + database.password);
                    break;
                //optional - depending on security Mechanism
                //we are not supporting this method so we'll skip bytes
                case CodePoint.NEWPASSWORD:
                    reader.skipBytes();
                    break;
                //optional - depending on security Mechanism
                case CodePoint.USRID:
                    database.userId = reader.readString();
                    if (SanityManager.DEBUG)
                        trace("USERID " + database.userId);
                    break;
                //optional - depending on security Mechanism
                case CodePoint.RDBNAM:
                    String dbname = parseRDBNAM();
                    if (database != null) {
                        if (database.getDatabaseName() == null) {
                            // we didn't get the RDBNAM on ACCSEC. Set it here
                            database.setDatabaseName(dbname);
                            session.addDatabase(database);
                            session.database = database;
                        } else if (!database.getDatabaseName().equals(dbname))
                            rdbnamMismatch(CodePoint.SECCHK);
                    } else {
                        // we should already have added the database in ACCSEC
                        // added code here in case we make the SECMEC session rather
                        // than database wide
                        initializeDatabase(dbname);
                    }
                    break;
                default:
                    invalidCodePoint(codePoint);

                }
                codePoint = reader.getCodePoint();
            }
            // check for SECMEC which is required
            if (securityMechanism == 0)
                missingCodePoint(CodePoint.SECMEC);

            // Check that we have a database name.
            if (database == null || database.getDatabaseName() == null)
                missingCodePoint(CodePoint.RDBNAM);

            //check if we have a userid and password when we need it
            if (securityCheckCode == 0 && (database.securityMechanism == CodePoint.SECMEC_USRIDPWD
                    || database.securityMechanism == CodePoint.SECMEC_USRIDONL)) {
                if (database.userId == null)
                    securityCheckCode = CodePoint.SECCHKCD_USERIDMISSING;
                else if (database.securityMechanism == CodePoint.SECMEC_USRIDPWD) {
                    if (database.password == null)
                        securityCheckCode = CodePoint.SECCHKCD_PASSWORDMISSING;
                }
                //Note, we'll ignore encryptedUserId and encryptedPassword if they
                //are also set
            }

            if (securityCheckCode == 0 && database.securityMechanism == CodePoint.SECMEC_USRSSBPWD) {
                if (database.userId == null)
                    securityCheckCode = CodePoint.SECCHKCD_USERIDMISSING;
                else if (database.passwordSubstitute == null)
                    securityCheckCode = CodePoint.SECCHKCD_PASSWORDMISSING;
            }

            if (securityCheckCode == 0 && database.securityMechanism == CodePoint.SECMEC_EUSRIDPWD) {
                if (database.decryptedUserId == null)
                    securityCheckCode = CodePoint.SECCHKCD_USERIDMISSING;
                else if (database.decryptedPassword == null)
                    securityCheckCode = CodePoint.SECCHKCD_PASSWORDMISSING;
            }

            if (securityCheckCode == 0 && database.securityMechanism == CodePoint.SECMEC_KERSEC) {
                if (gssContext.isEstablished()) {
                    try {
                        database.userId = gssContext.getSrcName().toString().split("@")[0];
                    } catch (GSSException e) {
                        handleException(e);
                    }
                }
            }
            // RESOLVE - when we do security we need to decrypt encrypted userid & password
            // before proceeding
        } // End "if (deferredReset) ... else ..." block

        // verify userid and password, if we haven't had any errors thus far.
        if ((securityCheckCode == 0) && (databaseAccessException == null)) {
            // DERBY-3596: Reset server side (embedded) physical connection for
            //     use with a new logical connection on the client.
            if (this.deferredReset) {
                // Reset the existing connection here.
                try {
                    database.getConnection().resetFromPool();
                    database.getConnection().setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
                    // Reset isolation level to default, as the client is in
                    // the process of creating a new logical connection.
                    database.getConnection().setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
                } catch (SQLException sqle) {
                    handleException(sqle);
                }
            } else {
                securityCheckCode = verifyUserIdPassword();
            }
        }

        // Security all checked 
        if (securityCheckCode == 0)
            session.setState(session.CHKSEC);

        return securityCheckCode;

    }

    /**
     * Write security check reply
     * Instance variables
     *    SVRCOD - serverity code - required
     *   SECCHKCD   - security check code  - required
     *   SECTKN - security token - optional, ignorable
     *   SVCERRNO   - security service error number
     *   SRVDGN   - Server Diagnostic Information
     *
     * @exception DRDAProtocolException
     */
    private void writeSECCHKRM(int securityCheckCode) throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.SECCHKRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, svrcodFromSecchkcd(securityCheckCode));
        writer.writeScalar1Byte(CodePoint.SECCHKCD, securityCheckCode);
        if (database.secTokenOut != null) {
            writer.writeScalarBytes(CodePoint.SECTKN, database.secTokenOut);
        }
        writer.endDdmAndDss();

        if (securityCheckCode != 0) {
            // then we have an error and are going to end up ignoring the rest
            // of the DSS request chain.
            skipRemainder(false);
        }

        finalizeChain();

    }

    /**
     * Calculate SVRCOD value from SECCHKCD
     *
     * @param securityCheckCode
     * @return SVRCOD value
     */
    private int svrcodFromSecchkcd(int securityCheckCode) {
        if (securityCheckCode == 0 || securityCheckCode == 2 || securityCheckCode == 5 || securityCheckCode == 8)
            return CodePoint.SVRCOD_INFO;
        else
            return CodePoint.SVRCOD_ERROR;
    }

    /**
     * Parse access RDB
     * Instance variables
     *   RDBACCCL - RDB Access Manager Class - required must be SQLAM
     *  CRRTKN - Correlation Token - required
     *  RDBNAM - Relational database name -required
     *  PRDID - Product specific identifier - required
     *  TYPDEFNAM   - Data Type Definition Name -required
     *  TYPDEFOVR   - Type definition overrides -required
     *  RDBALWUPD -  RDB Allow Updates optional
     *  PRDDTA - Product Specific Data - optional - ignorable
     *  STTDECDEL - Statement Decimal Delimiter - optional
     *  STTSTRDEL - Statement String Delimiter - optional
     *  TRGDFTRT - Target Default Value Return - optional
     *
     * @return severity code
     *
     * @exception DRDAProtocolException
     */
    private int parseACCRDB() throws DRDAProtocolException {
        int codePoint;
        int svrcod = 0;
        copyToRequired(ACCRDB_REQUIRED);
        reader.markCollection();
        codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            //required
            case CodePoint.RDBACCCL:
                checkLength(CodePoint.RDBACCCL, 2);
                int sqlam = reader.readNetworkShort();
                if (SanityManager.DEBUG)
                    trace("RDBACCCL = " + sqlam);
                // required to be SQLAM 

                if (sqlam != CodePoint.SQLAM)
                    invalidValue(CodePoint.RDBACCCL);
                removeFromRequired(CodePoint.RDBACCCL);
                break;
            //required
            case CodePoint.CRRTKN:
                database.crrtkn = reader.readBytes();
                if (SanityManager.DEBUG)
                    trace("crrtkn " + convertToHexString(database.crrtkn));
                removeFromRequired(CodePoint.CRRTKN);
                int l = database.crrtkn.length;
                if (l > CodePoint.MAX_NAME)
                    tooBig(CodePoint.CRRTKN);
                // the format of the CRRTKN is defined in the DRDA reference
                // x.yz where x is 1 to 8 bytes (variable)
                //       y is 1 to 8 bytes (variable)
                //      x is 6 bytes fixed
                //      size is variable between 9 and 23
                if (l < 9 || l > 23)
                    invalidValue(CodePoint.CRRTKN);
                byte[] part1 = new byte[l - 6];
                System.arraycopy(database.crrtkn, 0, part1, 0, part1.length);
                long time = SignedBinary.getLong(database.crrtkn, l - 8, SignedBinary.BIG_ENDIAN); // as "long" as unique
                session.drdaID = reader.convertBytes(part1) + time + leftBrace + session.connNum + rightBrace;
                if (SanityManager.DEBUG)
                    trace("******************************************drdaID is: " + session.drdaID);
                database.setDrdaID(session.drdaID);

                break;
            //required
            case CodePoint.RDBNAM:
                String dbname = parseRDBNAM();
                if (database != null) {
                    if (!database.getDatabaseName().equals(dbname))
                        rdbnamMismatch(CodePoint.ACCRDB);
                } else {
                    //first time we have seen a database name
                    Database d = session.getDatabase(dbname);
                    if (d == null)
                        initializeDatabase(dbname);
                    else {
                        database = d;
                        database.accessCount++;
                    }
                }
                removeFromRequired(CodePoint.RDBNAM);
                break;
            //required
            case CodePoint.PRDID:
                appRequester.setClientVersion(reader.readString());
                if (SanityManager.DEBUG)
                    trace("prdId " + appRequester.prdid);
                if (appRequester.prdid.length() > CodePoint.PRDID_MAX)
                    tooBig(CodePoint.PRDID);
                if (appRequester.getClientType() != appRequester.DNC_CLIENT) {
                    invalidClient(appRequester.prdid);
                }
                // All versions of DNC,the only client supported, handle
                // warnings on CNTQRY
                sendWarningsOnCNTQRY = true;
                // The client can not request DIAGLVL because when run with
                // an older server it will cause an exception. Older version
                // of the server do not recognize requests for DIAGLVL.
                if ((appRequester.getClientType() == appRequester.DNC_CLIENT)
                        && appRequester.greaterThanOrEqualTo(10, 2, 0)) {
                    diagnosticLevel = CodePoint.DIAGLVL1;
                }

                removeFromRequired(CodePoint.PRDID);
                break;
            //required
            case CodePoint.TYPDEFNAM:
                setStmtOrDbByteOrder(true, null, parseTYPDEFNAM());
                removeFromRequired(CodePoint.TYPDEFNAM);
                break;
            //required
            case CodePoint.TYPDEFOVR:
                parseTYPDEFOVR(null);
                removeFromRequired(CodePoint.TYPDEFOVR);
                break;
            //optional 
            case CodePoint.RDBALWUPD:
                checkLength(CodePoint.RDBALWUPD, 1);
                database.rdbAllowUpdates = readBoolean(CodePoint.RDBALWUPD);
                if (SanityManager.DEBUG)
                    trace("rdbAllowUpdates = " + database.rdbAllowUpdates);
                break;
            //optional, ignorable
            case CodePoint.PRDDTA:
                // for compression test
                parsePRDDTA();
                break;
            case CodePoint.TRGDFTRT:
                byte b = reader.readByte();
                if (b == (byte) 0xF1)
                    database.sendTRGDFTRT = true;
                break;
            //optional - not used in JCC so skip for now
            case CodePoint.STTDECDEL:
            case CodePoint.STTSTRDEL:
                codePointNotSupported(codePoint);
                break;
            default:
                invalidCodePoint(codePoint);
            }
            codePoint = reader.getCodePoint();
        }
        checkRequired(CodePoint.ACCRDB);
        // check that we can support the double-byte and mixed-byte CCSIDS
        // set svrcod to warning if they are not supported
        if ((database.ccsidDBC != 0 && !server.supportsCCSID(database.ccsidDBC))
                || (database.ccsidMBC != 0 && !server.supportsCCSID(database.ccsidMBC)))
            svrcod = CodePoint.SVRCOD_WARNING;
        return svrcod;
    }

    /**
     * Parse TYPDEFNAM
     *
     * @return typdefnam
     * @exception DRDAProtocolException
     */
    private String parseTYPDEFNAM() throws DRDAProtocolException {
        String typDefNam = reader.readString();
        if (SanityManager.DEBUG)
            trace("typeDefName " + typDefNam);
        if (typDefNam.length() > CodePoint.MAX_NAME)
            tooBig(CodePoint.TYPDEFNAM);
        checkValidTypDefNam(typDefNam);
        // check if the typedef is one we support
        if (!typDefNam.equals(CodePoint.TYPDEFNAM_QTDSQLASC) && !typDefNam.equals(CodePoint.TYPDEFNAM_QTDSQLJVM)
                && !typDefNam.equals(CodePoint.TYPDEFNAM_QTDSQLX86))
            valueNotSupported(CodePoint.TYPDEFNAM);
        return typDefNam;
    }

    /**
     * Set a statement or the database' byte order, depending on the arguments
     *
     * @param setDatabase   if true, set database' byte order, otherwise set statement's
     * @param stmt          DRDAStatement, used when setDatabase is false
     * @param typDefNam     TYPDEFNAM value
     */
    private void setStmtOrDbByteOrder(boolean setDatabase, DRDAStatement stmt, String typDefNam) {
        int byteOrder = (typDefNam.equals(CodePoint.TYPDEFNAM_QTDSQLX86) ? SignedBinary.LITTLE_ENDIAN
                : SignedBinary.BIG_ENDIAN);
        if (setDatabase) {
            database.typDefNam = typDefNam;
            database.byteOrder = byteOrder;
        } else {
            stmt.typDefNam = typDefNam;
            stmt.byteOrder = byteOrder;
        }
    }

    /**
     * Write Access to RDB Completed
     * Instance Variables
     *  SVRCOD - severity code - 0 info, 4 warning -required
     *  PRDID - product specific identifier -required
     *  TYPDEFNAM - type definition name -required
     *  TYPDEFOVR - type definition overrides - required
     *  RDBINTTKN - token which can be used to interrupt DDM commands - optional
     *  CRRTKN   - correlation token - only returned if we didn't get one from requester
     *  SRVDGN - server diagnostic information - optional
     *  PKGDFTCST - package default character subtype - optional
     *  USRID - User ID at the target system - optional
     *  SRVLST - Server List
     * 
     * @exception DRDAProtocolException
     */
    private void writeACCRDBRM(int svrcod) throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.ACCRDBRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, svrcod);
        writer.writeScalarString(CodePoint.PRDID, server.prdId);
        //TYPDEFNAM -required - JCC doesn't support QTDSQLJVM so for now we
        // just use ASCII, though we should eventually be able to use QTDSQLJVM
        // at level 7
        writer.writeScalarString(CodePoint.TYPDEFNAM, CodePoint.TYPDEFNAM_QTDSQLASC);
        writeTYPDEFOVR();
        writer.endDdmAndDss();

        // Write the initial piggy-backed data, currently the isolation level
        // and the schema name. Only write it if the client supports session
        // data caching.
        // Sending the session data on connection initialization was introduced
        // in Derby 10.7.
        if ((appRequester.getClientType() == appRequester.DNC_CLIENT)
                && appRequester.greaterThanOrEqualTo(10, 7, 0)) {
            try {
                writePBSD();
            } catch (SQLException se) {
                server.consoleExceptionPrint(se);
                errorInChain(se);
            }
        }
        finalizeChain();
    }

    private void writeTYPDEFOVR() throws DRDAProtocolException {
        //TYPDEFOVR - required - only single byte and mixed byte are specified
        writer.startDdm(CodePoint.TYPDEFOVR);
        writer.writeScalar2Bytes(CodePoint.CCSIDSBC, server.CCSIDSBC);
        writer.writeScalar2Bytes(CodePoint.CCSIDMBC, server.CCSIDMBC);
        // PKGDFTCST - Send character subtype and userid if requested
        if (database.sendTRGDFTRT) {
            // default to multibyte character
            writer.startDdm(CodePoint.PKGDFTCST);
            writer.writeShort(CodePoint.CSTMBCS);
            writer.endDdm();
            // userid
            writer.startDdm(CodePoint.USRID);
            writer.writeString(database.userId);
            writer.endDdm();
        }
        writer.endDdm();

    }

    /**
     * Parse Type Defintion Overrides
     *    TYPDEF Overrides specifies the Coded Character SET Identifiers (CCSIDs)
     *  that are in a named TYPDEF.
     * Instance Variables
     *  CCSIDSBC - CCSID for Single-Byte - optional
     *  CCSIDDBC - CCSID for Double-Byte - optional
     *  CCSIDMBC - CCSID for Mixed-byte characters -optional
     *
      * @param st   Statement this TYPDEFOVR applies to
     *
     * @exception DRDAProtocolException
     */
    private void parseTYPDEFOVR(DRDAStatement st) throws DRDAProtocolException {
        int codePoint;
        int ccsidSBC = 0;
        int ccsidDBC = 0;
        int ccsidMBC = 0;
        String ccsidSBCEncoding = null;
        String ccsidDBCEncoding = null;
        String ccsidMBCEncoding = null;

        reader.markCollection();

        codePoint = reader.getCodePoint();
        // at least one of the following instance variable is required
        // if the TYPDEFOVR is specified in a command object
        if (codePoint == -1 && st != null)
            missingCodePoint(CodePoint.CCSIDSBC);

        while (codePoint != -1) {
            switch (codePoint) {
            case CodePoint.CCSIDSBC:
                checkLength(CodePoint.CCSIDSBC, 2);
                ccsidSBC = reader.readNetworkShort();
                try {
                    ccsidSBCEncoding = CharacterEncodings.getJavaEncoding(ccsidSBC);
                } catch (Exception e) {
                    valueNotSupported(CodePoint.CCSIDSBC);
                }
                if (SanityManager.DEBUG)
                    trace("ccsidsbc = " + ccsidSBC + " encoding = " + ccsidSBCEncoding);
                break;
            case CodePoint.CCSIDDBC:
                checkLength(CodePoint.CCSIDDBC, 2);
                ccsidDBC = reader.readNetworkShort();
                try {
                    ccsidDBCEncoding = CharacterEncodings.getJavaEncoding(ccsidDBC);
                } catch (Exception e) {
                    // we write a warning later for this so no error
                    // unless for a statement
                    ccsidDBCEncoding = null;
                    if (st != null)
                        valueNotSupported(CodePoint.CCSIDSBC);
                }
                if (SanityManager.DEBUG)
                    trace("ccsiddbc = " + ccsidDBC + " encoding = " + ccsidDBCEncoding);
                break;
            case CodePoint.CCSIDMBC:
                checkLength(CodePoint.CCSIDMBC, 2);
                ccsidMBC = reader.readNetworkShort();
                try {
                    ccsidMBCEncoding = CharacterEncodings.getJavaEncoding(ccsidMBC);
                } catch (Exception e) {
                    // we write a warning later for this so no error
                    ccsidMBCEncoding = null;
                    if (st != null)
                        valueNotSupported(CodePoint.CCSIDMBC);
                }
                if (SanityManager.DEBUG)
                    trace("ccsidmbc = " + ccsidMBC + " encoding = " + ccsidMBCEncoding);
                break;
            default:
                invalidCodePoint(codePoint);

            }
            codePoint = reader.getCodePoint();
        }
        if (st == null) {
            if (ccsidSBC != 0) {
                database.ccsidSBC = ccsidSBC;
                database.ccsidSBCEncoding = ccsidSBCEncoding;
            }
            if (ccsidDBC != 0) {
                database.ccsidDBC = ccsidDBC;
                database.ccsidDBCEncoding = ccsidDBCEncoding;
            }
            if (ccsidMBC != 0) {
                database.ccsidMBC = ccsidMBC;
                database.ccsidMBCEncoding = ccsidMBCEncoding;
            }
        } else {
            if (ccsidSBC != 0) {
                st.ccsidSBC = ccsidSBC;
                st.ccsidSBCEncoding = ccsidSBCEncoding;
            }
            if (ccsidDBC != 0) {
                st.ccsidDBC = ccsidDBC;
                st.ccsidDBCEncoding = ccsidDBCEncoding;
            }
            if (ccsidMBC != 0) {
                st.ccsidMBC = ccsidMBC;
                st.ccsidMBCEncoding = ccsidMBCEncoding;
            }
        }
    }

    /**
     * Parse PRPSQLSTT - Prepare SQL Statement
     * Instance Variables
     *   RDBNAM - Relational Database Name - optional
     *   PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number - required
     *   RTNSQLDA - Return SQL Descriptor Area - optional
     *   MONITOR - Monitor events - optional.
     *   
     * @return return 0 - don't return sqlda, 1 - return input sqlda, 
     *       2 - return output sqlda
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private int parsePRPSQLSTT() throws DRDAProtocolException, SQLException {
        int codePoint;
        boolean rtnsqlda = false;
        boolean rtnOutput = true; // Return output SQLDA is default
        String typdefnam;
        Pkgnamcsn pkgnamcsn = null;

        DRDAStatement stmt = null;
        Database databaseToSet = null;

        reader.markCollection();

        codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            // optional
            case CodePoint.RDBNAM:
                setDatabase(CodePoint.PRPSQLSTT);
                databaseToSet = database;
                break;
            // required
            case CodePoint.PKGNAMCSN:
                pkgnamcsn = parsePKGNAMCSN();
                break;
            //optional
            case CodePoint.RTNSQLDA:
                // Return SQLDA with description of statement
                rtnsqlda = readBoolean(CodePoint.RTNSQLDA);
                break;
            //optional
            case CodePoint.TYPSQLDA:
                rtnOutput = parseTYPSQLDA();
                break;
            //optional
            case CodePoint.MONITOR:
                parseMONITOR();
                break;
            default:
                invalidCodePoint(codePoint);

            }
            codePoint = reader.getCodePoint();
        }

        stmt = database.newDRDAStatement(pkgnamcsn);
        String sqlStmt = parsePRPSQLSTTobjects(stmt);
        if (databaseToSet != null)
            stmt.setDatabase(database);
        stmt.explicitPrepare(sqlStmt);
        // set the statement as the current statement
        database.setCurrentStatement(stmt);

        if (!rtnsqlda)
            return 0;
        else if (rtnOutput)
            return 2;
        else
            return 1;
    }

    /**
     * Parse PRPSQLSTT objects
     * Objects
     *  TYPDEFNAM - Data type definition name - optional
     *  TYPDEFOVR - Type defintion overrides - optional
     *  SQLSTT - SQL Statement required
     *  SQLATTR - Cursor attributes on prepare - optional - level 7
     *
     * If TYPDEFNAM and TYPDEFOVR are supplied, they apply to the objects
     * sent with the statement.  Once the statement is over, the default values
     * sent in the ACCRDB are once again in effect.  If no values are supplied,
     * the values sent in the ACCRDB are used.
     * Objects may follow in one DSS or in several DSS chained together.
     * 
     * @return SQL statement
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private String parsePRPSQLSTTobjects(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        String sqlStmt = null;
        int codePoint;
        do {
            correlationID = reader.readDssHeader();
            while (reader.moreDssData()) {
                codePoint = reader.readLengthAndCodePoint(false);
                switch (codePoint) {
                // required
                case CodePoint.SQLSTT:
                    sqlStmt = parseEncodedString();
                    if (SanityManager.DEBUG)
                        trace("sqlStmt = " + sqlStmt);
                    break;
                // optional
                case CodePoint.TYPDEFNAM:
                    setStmtOrDbByteOrder(false, stmt, parseTYPDEFNAM());
                    break;
                // optional
                case CodePoint.TYPDEFOVR:
                    parseTYPDEFOVR(stmt);
                    break;
                // optional
                case CodePoint.SQLATTR:
                    parseSQLATTR(stmt);
                    break;
                default:
                    invalidCodePoint(codePoint);
                }
            }
        } while (reader.isChainedWithSameID());
        if (sqlStmt == null)
            missingCodePoint(CodePoint.SQLSTT);

        return sqlStmt;
    }

    /**
     * Parse TYPSQLDA - Type of the SQL Descriptor Area
     *
     * @return true if for output; false otherwise
     * @exception DRDAProtocolException
     */
    private boolean parseTYPSQLDA() throws DRDAProtocolException {
        checkLength(CodePoint.TYPSQLDA, 1);
        byte sqldaType = reader.readByte();
        if (SanityManager.DEBUG)
            trace("typSQLDa " + sqldaType);
        if (sqldaType == CodePoint.TYPSQLDA_STD_OUTPUT || sqldaType == CodePoint.TYPSQLDA_LIGHT_OUTPUT
                || sqldaType == CodePoint.TYPSQLDA_X_OUTPUT)
            return true;
        else if (sqldaType == CodePoint.TYPSQLDA_STD_INPUT || sqldaType == CodePoint.TYPSQLDA_LIGHT_INPUT
                || sqldaType == CodePoint.TYPSQLDA_X_INPUT)
            return false;
        else
            invalidValue(CodePoint.TYPSQLDA);

        // shouldn't get here but have to shut up compiler
        return false;
    }

    /**
     * Parse SQLATTR - Cursor attributes on prepare
     *   This is an encoded string. Can have combination of following, eg INSENSITIVE SCROLL WITH HOLD
     * Possible strings are
     *  SENSITIVE DYNAMIC SCROLL [FOR UPDATE]
     *  SENSITIVE STATIC SCROLL [FOR UPDATE]
     *  INSENSITIVE SCROLL
     *  FOR UPDATE
     *  WITH HOLD
     *
     * @param stmt DRDAStatement
     * @exception DRDAProtocolException
     */
    protected void parseSQLATTR(DRDAStatement stmt) throws DRDAProtocolException {
        String attrs = parseEncodedString();
        if (SanityManager.DEBUG)
            trace("sqlattr = '" + attrs + "'");
        //let Derby handle any errors in the types it doesn't support
        //just set the attributes

        boolean validAttribute = false;
        if (attrs.indexOf("INSENSITIVE SCROLL") != -1 || attrs.indexOf("SCROLL INSENSITIVE") != -1) //CLI
        {
            stmt.scrollType = ResultSet.TYPE_SCROLL_INSENSITIVE;
            stmt.concurType = ResultSet.CONCUR_READ_ONLY;
            validAttribute = true;
        }
        if ((attrs.indexOf("SENSITIVE DYNAMIC SCROLL") != -1) || (attrs.indexOf("SENSITIVE STATIC SCROLL") != -1)) {
            stmt.scrollType = ResultSet.TYPE_SCROLL_SENSITIVE;
            validAttribute = true;
        }

        if ((attrs.indexOf("FOR UPDATE") != -1)) {
            validAttribute = true;
            stmt.concurType = ResultSet.CONCUR_UPDATABLE;
        }

        if (attrs.indexOf("WITH HOLD") != -1) {
            stmt.withHoldCursor = ResultSet.HOLD_CURSORS_OVER_COMMIT;
            validAttribute = true;
        }

        if (!validAttribute) {
            invalidValue(CodePoint.SQLATTR);
        }
    }

    /**
     * Parse DSCSQLSTT - Describe SQL Statement previously prepared
     * Instance Variables
     *   TYPSQLDA - sqlda type expected (output or input)
     *  RDBNAM - relational database name - optional
     *  PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
     *  MONITOR - Monitor events - optional.
     *
     * @return expect "output sqlda" or not
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private boolean parseDSCSQLSTT() throws DRDAProtocolException, SQLException {
        int codePoint;
        boolean rtnOutput = true; // default
        Pkgnamcsn pkgnamcsn = null;
        reader.markCollection();

        codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            // optional
            case CodePoint.TYPSQLDA:
                rtnOutput = parseTYPSQLDA();
                break;
            // optional
            case CodePoint.RDBNAM:
                setDatabase(CodePoint.DSCSQLSTT);
                break;
            // required
            case CodePoint.PKGNAMCSN:
                pkgnamcsn = parsePKGNAMCSN();
                DRDAStatement stmt = database.getDRDAStatement(pkgnamcsn);
                if (stmt == null) {
                    invalidValue(CodePoint.PKGNAMCSN);
                }
                break;
            //optional
            case CodePoint.MONITOR:
                parseMONITOR();
                break;
            default:
                invalidCodePoint(codePoint);
            }
            codePoint = reader.getCodePoint();
        }
        if (pkgnamcsn == null)
            missingCodePoint(CodePoint.PKGNAMCSN);
        return rtnOutput;
    }

    /**
     * Parse EXCSQLSTT - Execute non-cursor SQL Statement previously prepared
     * Instance Variables
     *  RDBNAM - relational database name - optional
     *  PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
     *  OUTEXP - Output expected
     *  NBRROW - Number of rows to be inserted if it's an insert
     *  PRCNAM - procedure name if specified by host variable, not needed for Derby
     *  QRYBLKSZ - query block size
     *  MAXRSLCNT - max resultset count
     *  MAXBLKEXT - Max number of extra blocks
     *  RSLSETFLG - resultset flag
     *  RDBCMTOK - RDB Commit Allowed - optional
     *  OUTOVROPT - output override option
     *  QRYROWSET - Query Rowset Size - Level 7
     *  MONITOR - Monitor events - optional.
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void parseEXCSQLSTT() throws DRDAProtocolException, SQLException {
        int codePoint;
        String strVal;
        reader.markCollection();

        codePoint = reader.getCodePoint();
        boolean outputExpected = false;
        Pkgnamcsn pkgnamcsn = null;
        int numRows = 1; // default value
        int blkSize = 0;
        int maxrslcnt = 0; // default value
        int maxblkext = CodePoint.MAXBLKEXT_DEFAULT;
        int qryrowset = CodePoint.QRYROWSET_DEFAULT;
        int outovropt = CodePoint.OUTOVRFRS;
        byte[] rslsetflg = null;
        String procName = null;

        while (codePoint != -1) {
            switch (codePoint) {
            // optional
            case CodePoint.RDBNAM:
                setDatabase(CodePoint.EXCSQLSTT);
                break;
            // required
            case CodePoint.PKGNAMCSN:
                pkgnamcsn = parsePKGNAMCSN();
                break;
            // optional
            case CodePoint.OUTEXP:
                outputExpected = readBoolean(CodePoint.OUTEXP);
                if (SanityManager.DEBUG)
                    trace("outexp = " + outputExpected);
                break;
            // optional
            case CodePoint.NBRROW:
                checkLength(CodePoint.NBRROW, 4);
                numRows = reader.readNetworkInt();
                if (SanityManager.DEBUG)
                    trace("# of rows: " + numRows);
                break;
            // optional
            case CodePoint.PRCNAM:
                procName = reader.readString();
                if (SanityManager.DEBUG)
                    trace("Procedure Name = " + procName);
                break;
            // optional
            case CodePoint.QRYBLKSZ:
                blkSize = parseQRYBLKSZ();
                break;
            // optional
            case CodePoint.MAXRSLCNT:
                // this is the maximum result set count
                // values are 0 - requester is not capabable of receiving result
                // sets as reply data in the response to EXCSQLSTT
                // -1 - requester is able to receive all result sets
                checkLength(CodePoint.MAXRSLCNT, 2);
                maxrslcnt = reader.readNetworkShort();
                if (SanityManager.DEBUG)
                    trace("max rs count: " + maxrslcnt);
                break;
            // optional
            case CodePoint.MAXBLKEXT:
                // number of extra qury blocks of answer set data per result set
                // 0 - no extra query blocks
                // -1 - can receive entire result set
                checkLength(CodePoint.MAXBLKEXT, 2);
                maxblkext = reader.readNetworkShort();
                if (SanityManager.DEBUG)
                    trace("max extra blocks: " + maxblkext);
                break;
            // optional
            case CodePoint.RSLSETFLG:
                //Result set flags
                rslsetflg = reader.readBytes();
                for (int i = 0; i < rslsetflg.length; i++)
                    if (SanityManager.DEBUG)
                        trace("rslsetflg: " + rslsetflg[i]);
                break;
            // optional
            case CodePoint.RDBCMTOK:
                parseRDBCMTOK();
                break;
            // optional
            case CodePoint.OUTOVROPT:
                outovropt = parseOUTOVROPT();
                break;
            // optional
            case CodePoint.QRYROWSET:
                //Note minimum for OPNQRY is 0, we'll assume it is the same
                //for EXCSQLSTT though the standard doesn't say
                qryrowset = parseQRYROWSET(0);
                break;
            //optional
            case CodePoint.MONITOR:
                parseMONITOR();
                break;
            default:
                invalidCodePoint(codePoint);
            }
            codePoint = reader.getCodePoint();
        }

        if (pkgnamcsn == null)
            missingCodePoint(CodePoint.PKGNAMCSN);

        DRDAStatement stmt;
        boolean needPrepareCall = false;

        stmt = database.getDRDAStatement(pkgnamcsn);
        boolean isProcedure = (procName != null || (stmt != null && stmt.wasExplicitlyPrepared() && stmt.isCall));

        if (isProcedure) // stored procedure call
        {
            if (stmt == null || !(stmt.wasExplicitlyPrepared())) {
                stmt = database.newDRDAStatement(pkgnamcsn);
                stmt.setQryprctyp(CodePoint.QRYBLKCTL_DEFAULT);
                needPrepareCall = true;
            }

            stmt.procName = procName;
            stmt.outputExpected = outputExpected;
        } else {
            // we can't find the statement
            if (stmt == null) {
                invalidValue(CodePoint.PKGNAMCSN);
            }
            stmt.setQryprctyp(CodePoint.QRYBLKCTL_DEFAULT);
        }

        stmt.nbrrow = numRows;
        stmt.qryrowset = qryrowset;
        stmt.blksize = blkSize;
        stmt.maxblkext = maxblkext;
        stmt.maxrslcnt = maxrslcnt;
        stmt.outovropt = outovropt;
        stmt.rslsetflg = rslsetflg;
        if (pendingStatementTimeout >= 0) {
            stmt.getPreparedStatement().setQueryTimeout(pendingStatementTimeout);
            pendingStatementTimeout = -1;
        }

        // set the statement as the current statement
        database.setCurrentStatement(stmt);

        boolean hasResultSet;
        if (reader.isChainedWithSameID()) {
            hasResultSet = parseEXCSQLSTTobjects(stmt);
        } else {
            if (isProcedure && (needPrepareCall)) {
                // if we had parameters the callable statement would
                // be prepared with parseEXCQLSTTobjects, otherwise we
                // have to do it here
                String prepareString = "call " + stmt.procName + "()";
                if (SanityManager.DEBUG)
                    trace("$$$prepareCall is: " + prepareString);
                database.getConnection().clearWarnings();
                CallableStatement cs = (CallableStatement) stmt.prepare(prepareString);
            }
            stmt.ps.clearWarnings();
            hasResultSet = stmt.execute();
        }

        ResultSet rs = null;
        if (hasResultSet) {
            rs = stmt.getResultSet();
        }
        // temp until ps.execute() return value fixed
        hasResultSet = (rs != null);
        int numResults = 0;
        if (hasResultSet) {
            numResults = stmt.getNumResultSets();
            writeRSLSETRM(stmt);
        }

        // First of all, we send if there really are output params. Otherwise
        // CLI (.Net driver) fails. DRDA spec (page 151,152) says send SQLDTARD
        // if server has output param data to send.
        boolean sendSQLDTARD = stmt.hasOutputParams() && outputExpected;
        if (isProcedure) {
            if (sendSQLDTARD) {
                writer.createDssObject();
                writer.startDdm(CodePoint.SQLDTARD);
                writer.startDdm(CodePoint.FDODSC);
                writeQRYDSC(stmt, true);
                writer.endDdm();
                writer.startDdm(CodePoint.FDODTA);
                writeFDODTA(stmt);
                writer.endDdm();
                writer.endDdmAndDss();

                if (stmt.getExtDtaObjects() != null) {
                    // writeScalarStream() ends the dss
                    writeEXTDTA(stmt);
                }
            } else if (hasResultSet)
                // DRDA spec says that we MUST return either an
                // SQLDTARD or an SQLCARD--the former when we have
                // output parameters, the latter when we don't.
                // If we have a result set, then we have to write
                // the SQLCARD _now_, since it is expected before
                // we send the result set info below; if we don't
                // have a result set and we don't send SQLDTARD,
                // then we can wait until we reach the call to
                // checkWarning() below, which will write an
                // SQLCARD for us.
                writeNullSQLCARDobject();
        }

        //We need to marke that params are finished so that we know we 
        // are ready to send resultset info.
        stmt.finishParams();

        PreparedStatement ps = stmt.getPreparedStatement();
        int rsNum = 0;
        do {
            if (hasResultSet) {
                stmt.setCurrentDrdaResultSet(rsNum);
                //indicate that we are going to return data
                stmt.setQryrtndta(true);
                if (!isProcedure)
                    checkWarning(null, ps, null, -1, true, true);
                if (rsNum == 0)
                    writeSQLRSLRD(stmt);
                writeOPNQRYRM(true, stmt);
                writeSQLCINRD(stmt);
                writeQRYDSC(stmt, false);
                stmt.rsSuspend();

                /* Currently, if LMTBLKPRC is used, a pre-condition is that no lob columns.
                 * But in the future, when we do support LOB in LMTBLKPRC, the drda spec still
                 * does not allow LOB to be sent with OPNQRYRM.  So this "if" here will have
                 * to add "no lob columns".
                 */
                if (stmt.getQryprctyp() == CodePoint.LMTBLKPRC)
                    writeQRYDTA(stmt);
            } else if (!sendSQLDTARD) {
                int updateCount = ps.getUpdateCount();
                if (false && (database.RDBUPDRM_sent == false) && !isProcedure) {
                    writeRDBUPDRM();
                }

                checkWarning(database.getConnection(), stmt.ps, null, updateCount, true, true);
            }

        } while (hasResultSet && (++rsNum < numResults));

        return; // we are done
    }

    /**
     * Parse RDBCMTOK - tells the database whether to allow commits or rollbacks
     * to be executed as part of the command
     * Since we don't have a SQL commit or rollback command, we will just ignore
     * this for now
     *
     * @exception DRDAProtocolException
     */
    private void parseRDBCMTOK() throws DRDAProtocolException {
        boolean rdbcmtok = readBoolean(CodePoint.RDBCMTOK);
        if (SanityManager.DEBUG)
            trace("rdbcmtok = " + rdbcmtok);
    }

    /**
     * Parse EXCSQLSTT command objects
     * Command Objects
     *  TYPDEFNAM - Data Type Definition Name - optional
     *  TYPDEFOVR - TYPDEF Overrides -optional
     *   SQLDTA - optional, variable data, specified if prpared statement has input parameters
     *   EXTDTA - optional, externalized FD:OCA data
     *   OUTOVR - output override descriptor, not allowed for stored procedure calls
     *
     * If TYPDEFNAM and TYPDEFOVR are supplied, they apply to the objects
     * sent with the statement.  Once the statement is over, the default values
     * sent in the ACCRDB are once again in effect.  If no values are supplied,
     * the values sent in the ACCRDB are used.
     * Objects may follow in one DSS or in several DSS chained together.
     * 
     * @param stmt   the DRDAStatement to execute
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private boolean parseEXCSQLSTTobjects(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        int codePoint;
        boolean gotSQLDTA = false, gotEXTDTA = false;
        boolean result = false;
        do {
            correlationID = reader.readDssHeader();
            while (reader.moreDssData()) {
                codePoint = reader.readLengthAndCodePoint(true);
                switch (codePoint) {
                // optional
                case CodePoint.TYPDEFNAM:
                    setStmtOrDbByteOrder(false, stmt, parseTYPDEFNAM());
                    stmt.setTypDefValues();
                    break;
                // optional
                case CodePoint.TYPDEFOVR:
                    parseTYPDEFOVR(stmt);
                    stmt.setTypDefValues();
                    break;
                // required
                case CodePoint.SQLDTA:
                    parseSQLDTA(stmt);
                    gotSQLDTA = true;
                    break;
                // optional
                case CodePoint.EXTDTA:
                    readAndSetAllExtParams(stmt, true);
                    stmt.ps.clearWarnings();
                    result = stmt.execute();
                    gotEXTDTA = true;
                    break;
                // optional
                case CodePoint.OUTOVR:
                    parseOUTOVR(stmt);
                    break;
                default:
                    invalidCodePoint(codePoint);
                }
            }
        } while (reader.isChainedWithSameID());

        // SQLDTA is required
        if (!gotSQLDTA)
            missingCodePoint(CodePoint.SQLDTA);

        if (!gotEXTDTA) {
            stmt.ps.clearWarnings();
            result = stmt.execute();
        }

        return result;
    }

    /**
     * Write SQLCINRD - result set column information
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void writeSQLCINRD(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        ResultSet rs = stmt.getResultSet();

        writer.createDssObject();
        writer.startDdm(CodePoint.SQLCINRD);
        if (sqlamLevel >= MGRLVL_7)
            writeSQLDHROW(((EngineResultSet) rs).getHoldability());

        ResultSetMetaData rsmeta = rs.getMetaData();
        int ncols = rsmeta.getColumnCount();
        writer.writeShort(ncols); // num of columns
        if (sqlamLevel >= MGRLVL_7) {
            for (int i = 0; i < ncols; i++)
                writeSQLDAGRP(rsmeta, null, i, true);
        } else {
            for (int i = 0; i < ncols; i++) {
                writeVCMorVCS(rsmeta.getColumnName(i + 1));
                writeVCMorVCS(rsmeta.getColumnLabel(i + 1));
                writeVCMorVCS(null);
            }
        }
        writer.endDdmAndDss();
    }

    /**
     * Write SQLRSLRD - result set reply data
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void writeSQLRSLRD(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        int numResults = stmt.getNumResultSets();

        writer.createDssObject();
        writer.startDdm(CodePoint.SQLRSLRD);
        writer.writeShort(numResults); // num of result sets

        for (int i = 0; i < numResults; i++) {
            writer.writeInt(i); // rsLocator
            writeVCMorVCS(stmt.getResultSetCursorName(i));
            writer.writeInt(1); // num of rows XXX resolve, it doesn't matter for now

        }
        writer.endDdmAndDss();
    }

    /**
     * Write RSLSETRM
     * Instance variables
     *  SVRCOD - Severity code - Information only - required
     *  PKGSNLST - list of PKGNAMCSN -required
     *  SRVDGN - Server Diagnostic Information -optional
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void writeRSLSETRM(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        int numResults = stmt.getNumResultSets();
        writer.createDssReply();
        writer.startDdm(CodePoint.RSLSETRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, 0);
        writer.startDdm(CodePoint.PKGSNLST);

        for (int i = 0; i < numResults; i++)
            writePKGNAMCSN(stmt.getResultSetPkgcnstkn(i).getBytes());
        writer.endDdm();
        writer.endDdmAndDss();
    }

    /**
     * Parse SQLDTA - SQL program variable data 
     * and handle exception.
     * @see #parseSQLDTA_work
     */

    private void parseSQLDTA(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        try {
            parseSQLDTA_work(stmt);
        } catch (SQLException se) {
            skipRemainder(true);
            throw se;
        }
    }

    /**
     * Parse SQLDTA - SQL program variable data
     * Instance Variables
     *  FDODSC - FD:OCA data descriptor - required
     *  FDODTA - FD:OCA data - optional
     *    
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void parseSQLDTA_work(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        String strVal;
        PreparedStatement ps = stmt.getPreparedStatement();
        int codePoint;
        ParameterMetaData pmeta = null;

        // Clear params without releasing storage
        stmt.clearDrdaParams();

        int numVars = 0;
        boolean rtnParam = false;

        reader.markCollection();
        codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            // required
            case CodePoint.FDODSC:
                while (reader.getDdmLength() > 6) //we get parameter info til last 6 byte
                {
                    int dtaGrpLen = reader.readUnsignedByte();
                    int numVarsInGrp = (dtaGrpLen - 3) / 3;
                    if (SanityManager.DEBUG)
                        trace("num of vars in this group is: " + numVarsInGrp);
                    reader.readByte(); // tripletType
                    reader.readByte(); // id
                    for (int j = 0; j < numVarsInGrp; j++) {
                        final byte t = reader.readByte();
                        if (SanityManager.DEBUG)
                            trace("drdaType is: " + "0x" + Integer.toHexString(t));
                        int drdaLength = reader.readNetworkShort();
                        if (SanityManager.DEBUG)
                            trace("drdaLength is: " + drdaLength);
                        stmt.addDrdaParam(t, drdaLength);
                    }
                }
                numVars = stmt.getDrdaParamCount();
                if (SanityManager.DEBUG)
                    trace("numVars = " + numVars);
                if (ps == null) // it is a CallableStatement under construction
                {
                    StringBuffer marks = new StringBuffer(); // construct parameter marks
                    marks.append("(?");
                    for (int i = 1; i < numVars; i++)
                        marks.append(", ?");
                    String prepareString = "call " + stmt.procName + marks.toString() + ")";
                    if (SanityManager.DEBUG)
                        trace("$$ prepareCall is: " + prepareString);
                    CallableStatement cs = null;
                    try {
                        cs = (CallableStatement) stmt.prepare(prepareString);
                        stmt.registerAllOutParams();
                    } catch (SQLException se) {
                        if (!stmt.outputExpected || (!se.getSQLState().equals(SQLState.LANG_NO_METHOD_FOUND)))
                            throw se;
                        if (SanityManager.DEBUG)
                            trace("****** second try with return parameter...");
                        // Save first SQLException most likely suspect
                        if (numVars == 1)
                            prepareString = "? = call " + stmt.procName + "()";
                        else
                            prepareString = "? = call " + stmt.procName + "(" + marks.substring(3) + ")";
                        if (SanityManager.DEBUG)
                            trace("$$ prepareCall is: " + prepareString);
                        try {
                            cs = (CallableStatement) stmt.prepare(prepareString);
                        } catch (SQLException se2) {
                            // The first exception is the most likely suspect
                            throw se;
                        }
                        rtnParam = true;
                    }
                    ps = cs;
                    stmt.ps = ps;
                }

                pmeta = stmt.getParameterMetaData();

                reader.readBytes(6); // descriptor footer
                break;
            // optional
            case CodePoint.FDODTA:
                reader.readByte(); // row indicator
                for (int i = 0; i < numVars; i++) {

                    if ((stmt.getParamDRDAType(i + 1) & 0x1) == 0x1) // nullable
                    {
                        int nullData = reader.readUnsignedByte();
                        if ((nullData & 0xFF) == FdocaConstants.NULL_DATA) {
                            if (SanityManager.DEBUG)
                                trace("******param null");
                            if (pmeta.getParameterMode(i + 1) != JDBC30Translation.PARAMETER_MODE_OUT)
                                ps.setNull(i + 1, pmeta.getParameterType(i + 1));
                            if (stmt.isOutputParam(i + 1))
                                stmt.registerOutParam(i + 1);
                            continue;
                        }
                    }

                    // not null, read and set it
                    readAndSetParams(i, stmt, pmeta);
                }
                break;
            case CodePoint.EXTDTA:
                readAndSetAllExtParams(stmt, false);
                break;
            default:
                invalidCodePoint(codePoint);

            }
            codePoint = reader.getCodePoint();
        }

    }

    private int getByteOrder() {
        DRDAStatement stmt = database.getCurrentStatement();
        return ((stmt != null && stmt.typDefNam != null) ? stmt.byteOrder : database.byteOrder);
    }

    /** A cached {@code Calendar} instance using the GMT time zone. */
    private Calendar gmtCalendar;

    /**
     * Get a {@code Calendar} instance with time zone set to GMT. The instance
     * is cached for reuse by this thread. This calendar can be used to
     * consistently read and write date and time values using the same
     * calendar. Since the local default calendar may not be able to represent
     * all times (for instance because the time would fall into a non-existing
     * hour of the day when switching to daylight saving time, see DERBY-4582),
     * we use the GMT time zone which doesn't observe daylight saving time.
     *
     * @return a calendar in the GMT time zone
     */
    private Calendar getGMTCalendar() {
        if (gmtCalendar == null) {
            TimeZone gmt = TimeZone.getTimeZone("GMT");
            gmtCalendar = Calendar.getInstance(gmt);
        }
        return gmtCalendar;
    }

    /**
     * Read different types of input parameters and set them in
     * PreparedStatement
     * @param i         index of the parameter
     * @param stmt      drda statement
     * @param pmeta      parameter meta data
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void readAndSetParams(int i, DRDAStatement stmt, ParameterMetaData pmeta)
            throws DRDAProtocolException, SQLException {
        PreparedStatement ps = stmt.getPreparedStatement();

        // mask out null indicator
        final int drdaType = ((stmt.getParamDRDAType(i + 1) | 0x01) & 0xff);
        final int paramLenNumBytes = stmt.getParamLen(i + 1);

        if (ps instanceof CallableStatement) {
            if (stmt.isOutputParam(i + 1)) {
                CallableStatement cs = (CallableStatement) ps;
                cs.registerOutParameter(i + 1, stmt.getOutputParamType(i + 1));
            }
        }

        switch (drdaType) {
        case DRDAConstants.DRDA_TYPE_NBOOLEAN: {
            boolean paramVal = (reader.readByte() == 1);
            if (SanityManager.DEBUG)
                trace("boolean parameter value is: " + paramVal);
            ps.setBoolean(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NSMALL: {
            short paramVal = (short) reader.readShort(getByteOrder());
            if (SanityManager.DEBUG)
                trace("short parameter value is: " + paramVal);
            ps.setShort(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NINTEGER: {
            int paramVal = reader.readInt(getByteOrder());
            if (SanityManager.DEBUG)
                trace("integer parameter value is: " + paramVal);
            ps.setInt(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NINTEGER8: {
            long paramVal = reader.readLong(getByteOrder());
            if (SanityManager.DEBUG)
                trace("parameter value is: " + paramVal);
            ps.setLong(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NFLOAT4: {
            float paramVal = reader.readFloat(getByteOrder());
            if (SanityManager.DEBUG)
                trace("parameter value is: " + paramVal);
            ps.setFloat(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NFLOAT8: {
            double paramVal = reader.readDouble(getByteOrder());
            if (SanityManager.DEBUG)
                trace("nfloat8 parameter value is: " + paramVal);
            ps.setDouble(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NDECIMAL: {
            int precision = (paramLenNumBytes >> 8) & 0xff;
            int scale = paramLenNumBytes & 0xff;
            BigDecimal paramVal = reader.readBigDecimal(precision, scale);
            if (SanityManager.DEBUG)
                trace("ndecimal parameter value is: " + paramVal);
            ps.setBigDecimal(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NDATE: {
            String paramVal = reader.readStringData(10).trim(); //parameter may be char value
            if (SanityManager.DEBUG)
                trace("ndate parameter value is: \"" + paramVal + "\"");
            try {
                Calendar cal = getGMTCalendar();
                ps.setDate(i + 1, parseDate(paramVal, cal), cal);
            } catch (java.lang.IllegalArgumentException e) {
                // Just use SQLSTATE as message since, if user wants to
                // retrieve it, the message will be looked up by the
                // sqlcamessage() proc, which will get the localized
                // message based on SQLSTATE, and will ignore the
                // the message we use here...
                throw new SQLException(SQLState.LANG_DATE_SYNTAX_EXCEPTION,
                        SQLState.LANG_DATE_SYNTAX_EXCEPTION.substring(0, 5));
            }
            break;
        }
        case DRDAConstants.DRDA_TYPE_NTIME: {
            String paramVal = reader.readStringData(8).trim(); //parameter may be char value
            if (SanityManager.DEBUG)
                trace("ntime parameter value is: " + paramVal);
            try {
                Calendar cal = getGMTCalendar();
                ps.setTime(i + 1, parseTime(paramVal, cal), cal);
            } catch (java.lang.IllegalArgumentException e) {
                throw new SQLException(SQLState.LANG_DATE_SYNTAX_EXCEPTION,
                        SQLState.LANG_DATE_SYNTAX_EXCEPTION.substring(0, 5));
            }
            break;
        }
        case DRDAConstants.DRDA_TYPE_NTIMESTAMP: {
            // JCC represents ts in a slightly different format than Java standard, so
            // we do the conversion to Java standard here.
            int timestampLength = appRequester.getTimestampLength();

            String paramVal = reader.readStringData(timestampLength).trim(); //parameter may be char value
            if (SanityManager.DEBUG)
                trace("ntimestamp parameter value is: " + paramVal);
            try {
                Calendar cal = getGMTCalendar();
                ps.setTimestamp(i + 1, parseTimestamp(paramVal, cal), cal);
            } catch (java.lang.IllegalArgumentException e1) {
                // thrown by parseTimestamp(...) for bad syntax...
                throw new SQLException(SQLState.LANG_DATE_SYNTAX_EXCEPTION,
                        SQLState.LANG_DATE_SYNTAX_EXCEPTION.substring(0, 5));
            }
            break;
        }
        case DRDAConstants.DRDA_TYPE_NCHAR:
        case DRDAConstants.DRDA_TYPE_NVARCHAR:
        case DRDAConstants.DRDA_TYPE_NLONG:
        case DRDAConstants.DRDA_TYPE_NVARMIX:
        case DRDAConstants.DRDA_TYPE_NLONGMIX: {
            String paramVal = reader.readLDStringData(stmt.ccsidMBCEncoding);
            if (SanityManager.DEBUG)
                trace("char/varchar parameter value is: " + paramVal);
            ps.setString(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NFIXBYTE: {
            byte[] paramVal = reader.readBytes();
            if (SanityManager.DEBUG)
                trace("fix bytes parameter value is: " + convertToHexString(paramVal));
            ps.setBytes(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NVARBYTE:
        case DRDAConstants.DRDA_TYPE_NLONGVARBYTE: {
            int length = reader.readNetworkShort(); //protocol control data always follows big endian
            if (SanityManager.DEBUG)
                trace("===== binary param length is: " + length);
            byte[] paramVal = reader.readBytes(length);
            ps.setBytes(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NUDT: {
            Object paramVal = readUDT();
            ps.setObject(i + 1, paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NARRAY: {
            Object paramVal = readUDT();
            ps.setArray(i + 1, (Array) paramVal);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NLOBBYTES:
        case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
        case DRDAConstants.DRDA_TYPE_NLOBCSBCS:
        case DRDAConstants.DRDA_TYPE_NLOBCDBCS: {
            long length = readLobLength(paramLenNumBytes);
            if (length != 0) //can be -1 for CLI if "data at exec" mode, see clifp/exec test
            {
                stmt.addExtPosition(i);
            } else /* empty */
            {
                if (drdaType == DRDAConstants.DRDA_TYPE_NLOBBYTES)
                    ps.setBytes(i + 1, new byte[0]);
                else
                    ps.setString(i + 1, "");
            }
            break;
        }
        case DRDAConstants.DRDA_TYPE_NLOBLOC: {
            //read the locator value
            int paramVal = reader.readInt(getByteOrder());

            if (SanityManager.DEBUG)
                trace("locator value is: " + paramVal);

            //Map the locator value to the Blob object in the
            //Hash map.
            java.sql.Blob blobFromLocator = (java.sql.Blob) database.getConnection().getLOBMapping(paramVal);

            //set the PreparedStatement parameter to the mapped
            //Blob object.
            ps.setBlob(i + 1, blobFromLocator);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NCLOBLOC: {
            //read the locator value.
            int paramVal = reader.readInt(getByteOrder());

            if (SanityManager.DEBUG)
                trace("locator value is: " + paramVal);

            //Map the locator value to the Clob object in the
            //Hash Map.
            java.sql.Clob clobFromLocator = (java.sql.Clob) database.getConnection().getLOBMapping(paramVal);

            //set the PreparedStatement parameter to the mapped
            //Clob object.
            ps.setClob(i + 1, clobFromLocator);
            break;
        }
        case DRDAConstants.DRDA_TYPE_NROWID: {
            byte[] b = reader.readBytes();
            SQLRowId paramVal = new SQLRowId(b);
            ps.setRowId(i + 1, paramVal);
            break;
        }
        default: {
            String paramVal = reader.readLDStringData(stmt.ccsidMBCEncoding);
            if (SanityManager.DEBUG)
                trace("default type parameter value is: " + paramVal);
            ps.setObject(i + 1, paramVal);
        }
        }
    }

    /** Read a UDT from the stream */
    private Object readUDT() throws DRDAProtocolException {
        int length = reader.readNetworkShort(); //protocol control data always follows big endian
        if (SanityManager.DEBUG) {
            trace("===== udt param length is: " + length);
        }
        byte[] bytes = reader.readBytes(length);

        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);

            return ois.readObject();
        } catch (Exception e) {
            markCommunicationsFailure(e, "DRDAConnThread.readUDT()", "", e.getMessage(), "*");
            return null;
        }
    }

    private long readLobLength(int extLenIndicator) throws DRDAProtocolException {
        switch (extLenIndicator) {
        case 0x8002:
            return (long) reader.readNetworkShort();
        case 0x8004:
            return (long) reader.readNetworkInt();
        case 0x8006:
            return (long) reader.readNetworkSixByteLong();
        case 0x8008:
            return (long) reader.readNetworkLong();
        default:
            throwSyntaxrm(CodePoint.SYNERRCD_INCORRECT_EXTENDED_LEN, extLenIndicator);
            return 0L;
        }

    }

    /**
     * Parse a date string as it is received from the client.
     *
     * @param dateString the date string to parse
     * @param cal the calendar in which the date is parsed
     * @return a Date object representing the date in the specified calendar
     * @see com.splicemachine.db.client.am.DateTime#dateToDateBytes
     * @throws IllegalArgumentException if the date is not correctly formatted
     */
    private java.sql.Date parseDate(String dateString, Calendar cal) {
        // Get each component out of YYYY-MM-DD
        String[] components = dateString.split("-");
        if (components.length != 3) {
            throw new IllegalArgumentException();
        }

        cal.clear();

        // Set date components
        cal.set(Calendar.YEAR, Integer.parseInt(components[0]));
        cal.set(Calendar.MONTH, Integer.parseInt(components[1]) - 1);
        cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(components[2]));

        // Normalize time components as specified by java.sql.Date
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        return new java.sql.Date(cal.getTimeInMillis());
    }

    /**
     * Parse a time string as it is received from the client.
     *
     * @param timeString the time string to parse
     * @param cal the calendar in which the time is parsed
     * @return a Date object representing the time in the specified calendar
     * @see com.splicemachine.db.client.am.DateTime#timeToTimeBytes
     * @throws IllegalArgumentException if the time is not correctly formatted
     */
    private Time parseTime(String timeString, Calendar cal) {
        // Get each component out of HH:MM:SS
        String[] components = timeString.split(":");
        if (components.length != 3) {
            throw new IllegalArgumentException();
        }

        cal.clear();

        // Normalize date components as specified by java.sql.Time
        cal.set(Calendar.YEAR, 1970);
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DAY_OF_MONTH, 1);

        // Set time components
        cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(components[0]));
        cal.set(Calendar.MINUTE, Integer.parseInt(components[1]));
        cal.set(Calendar.SECOND, Integer.parseInt(components[2]));

        // No millisecond resolution for Time
        cal.set(Calendar.MILLISECOND, 0);

        return new Time(cal.getTimeInMillis());
    }

    /**
     * Parse a timestamp string as it is received from the client.
     *
     * @param timeString the time string to parse
     * @param cal the calendar in which the timestamp is parsed
     * @return a Date object representing the timestamp in the specified
     * calendar
     * @see com.splicemachine.db.client.am.DateTime#timestampToTimestampBytes
     * @throws IllegalArgumentException if the timestamp is not correctly
     * formatted
     */
    private Timestamp parseTimestamp(String timeString, Calendar cal) {
        // Get each component out of YYYY-MM-DD-HH.MM.SS.fffffffff
        String[] components = PARSE_TIMESTAMP_PATTERN.split(timeString);
        if (components.length != 7) {
            throw new IllegalArgumentException();
        }

        cal.clear();
        cal.set(Calendar.YEAR, Integer.parseInt(components[0]));
        cal.set(Calendar.MONTH, Integer.parseInt(components[1]) - 1);
        cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(components[2]));
        cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(components[3]));
        cal.set(Calendar.MINUTE, Integer.parseInt(components[4]));
        cal.set(Calendar.SECOND, Integer.parseInt(components[5]));

        int nanos = 0;

        final int radix = 10;
        String nanoString = components[6];

        // Get up to nine digits from the nano second component
        for (int i = 0; i < 9; i++) {
            // Scale up the intermediate result
            nanos *= radix;

            // Add the next digit, if there is one. Continue the loop even if
            // there are no more digits, since we still need to scale up the
            // intermediate result as if the fraction part were padded with
            // zeros.
            if (i < nanoString.length()) {
                int digit = Character.digit(nanoString.charAt(i), radix);
                if (digit == -1) {
                    // not a digit
                    throw new IllegalArgumentException();
                }
                nanos += digit;
            }
        }

        Timestamp ts = new Timestamp(cal.getTimeInMillis());
        ts.setNanos(nanos);
        return ts;
    }

    private void readAndSetAllExtParams(final DRDAStatement stmt, final boolean streamLOB)
            throws SQLException, DRDAProtocolException {
        final int numExt = stmt.getExtPositionCount();
        for (int i = 0; i < numExt; i++) {
            int paramPos = stmt.getExtPosition(i);
            // Only the last EXTDTA is streamed.  This is because all of 
            // the parameters have to be set before execution and are 
            // consecutive in the network server stream, so only the last
            // one can be streamed.
            final boolean doStreamLOB = (streamLOB && i == numExt - 1);
            readAndSetExtParam(paramPos, stmt, stmt.getParamDRDAType(paramPos + 1), stmt.getParamLen(paramPos + 1),
                    doStreamLOB);
            // Each extdta in it's own dss
            if (i < numExt - 1) {
                correlationID = reader.readDssHeader();
                int codePoint = reader.readLengthAndCodePoint(true);
            }
        }

    }

    /**
     * Read different types of input parameters and set them in PreparedStatement
      * @param i zero-based index of the parameter
     * @param stmt         associated ps
     * @param drdaType   drda type of the parameter
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void readAndSetExtParam(int i, DRDAStatement stmt, int drdaType, int extLen, boolean streamLOB)
            throws DRDAProtocolException, SQLException {
        // Note the switch from zero-based to one-based index below.
        drdaType = (drdaType & 0x000000ff); // need unsigned value
        boolean checkNullability = false;
        if (sqlamLevel >= MGRLVL_7 && FdocaConstants.isNullable(drdaType))
            checkNullability = true;

        final EXTDTAReaderInputStream stream = reader.getEXTDTAReaderInputStream(checkNullability);

        // Determine encoding first, mostly for debug/tracing purposes
        String encoding = "na";
        switch (drdaType) {
        case DRDAConstants.DRDA_TYPE_LOBCSBCS:
        case DRDAConstants.DRDA_TYPE_NLOBCSBCS:
            encoding = stmt.ccsidSBCEncoding;
            break;
        case DRDAConstants.DRDA_TYPE_LOBCDBCS:
        case DRDAConstants.DRDA_TYPE_NLOBCDBCS:
            encoding = stmt.ccsidDBCEncoding;
            break;
        case DRDAConstants.DRDA_TYPE_LOBCMIXED:
        case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
            encoding = stmt.ccsidMBCEncoding;
            break;
        }

        traceEXTDTARead(drdaType, i + 1, stream, streamLOB, encoding);

        try {
            switch (drdaType) {
            case DRDAConstants.DRDA_TYPE_LOBBYTES:
            case DRDAConstants.DRDA_TYPE_NLOBBYTES:
                setAsBinaryStream(stmt, i + 1, stream, streamLOB);
                break;
            case DRDAConstants.DRDA_TYPE_LOBCSBCS:
            case DRDAConstants.DRDA_TYPE_NLOBCSBCS:
            case DRDAConstants.DRDA_TYPE_LOBCDBCS:
            case DRDAConstants.DRDA_TYPE_NLOBCDBCS:
            case DRDAConstants.DRDA_TYPE_LOBCMIXED:
            case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
                setAsCharacterStream(stmt, i + 1, stream, streamLOB, encoding);
                break;
            default:
                invalidValue(drdaType);
            }

        } catch (java.io.UnsupportedEncodingException e) {
            throw new SQLException(e.getMessage());

        } catch (IOException e) {
            throw new SQLException(e.getMessage());

        }
    }

    /**
     * Parse EXCSQLIMM - Execute Immediate Statement
     * Instance Variables
     *  RDBNAM - relational database name - optional
     *  PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
     *  RDBCMTOK - RDB Commit Allowed - optional
     *  MONITOR - Monitor Events - optional
     *
     * Command Objects
     *  TYPDEFNAM - Data Type Definition Name - optional
     *  TYPDEFOVR - TYPDEF Overrides -optional
     *  SQLSTT - SQL Statement -required
     *
     * @return update count
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private int parseEXCSQLIMM() throws DRDAProtocolException, SQLException {
        int codePoint;
        reader.markCollection();
        Pkgnamcsn pkgnamcsn = null;
        codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            // optional
            case CodePoint.RDBNAM:
                setDatabase(CodePoint.EXCSQLIMM);
                break;
            // required
            case CodePoint.PKGNAMCSN:
                pkgnamcsn = parsePKGNAMCSN();
                break;
            case CodePoint.RDBCMTOK:
                parseRDBCMTOK();
                break;
            //optional
            case CodePoint.MONITOR:
                parseMONITOR();
                break;
            default:
                invalidCodePoint(codePoint);

            }
            codePoint = reader.getCodePoint();
        }
        DRDAStatement drdaStmt = database.getDefaultStatement(pkgnamcsn);
        // initialize statement for reuse
        drdaStmt.initialize();
        String sqlStmt = parseEXECSQLIMMobjects();
        Statement statement = drdaStmt.getStatement();
        statement.clearWarnings();
        if (pendingStatementTimeout >= 0) {
            statement.setQueryTimeout(pendingStatementTimeout);
            pendingStatementTimeout = -1;
        }
        return statement.executeUpdate(sqlStmt);
    }

    /**
     * Parse EXCSQLSET - Execute Set SQL Environment
     * Instance Variables
     *  RDBNAM - relational database name - optional
     *  PKGNAMCT - RDB Package Name, Consistency Token  - optional
     *  MONITOR - Monitor Events - optional
     *
     * Command Objects
     *  TYPDEFNAM - Data Type Definition Name - required
     *  TYPDEFOVR - TYPDEF Overrides - required
     *  SQLSTT - SQL Statement - required (at least one; may be more)
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private boolean parseEXCSQLSET() throws DRDAProtocolException, SQLException {

        int codePoint;
        reader.markCollection();

        codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            // optional
            case CodePoint.RDBNAM:
                setDatabase(CodePoint.EXCSQLSET);
                break;
            // optional
            case CodePoint.PKGNAMCT:
                // we are going to ignore this for EXCSQLSET
                // since we are just going to reuse an existing statement
                String pkgnamct = parsePKGNAMCT();
                break;
            // optional
            case CodePoint.MONITOR:
                parseMONITOR();
                break;
            // required
            case CodePoint.PKGNAMCSN:
                // we are going to ignore this for EXCSQLSET.
                // since we are just going to reuse an existing statement.
                // NOTE: This codepoint is not in the DDM spec for 'EXCSQLSET',
                // but since it DOES get sent by jcc1.2, we have to have
                // a case for it...
                Pkgnamcsn pkgnamcsn = parsePKGNAMCSN();
                break;
            default:
                invalidCodePoint(codePoint);

            }
            codePoint = reader.getCodePoint();
        }

        parseEXCSQLSETobjects();
        return true;
    }

    /**
     * Parse EXCSQLIMM objects
     * Objects
     *  TYPDEFNAM - Data type definition name - optional
     *  TYPDEFOVR - Type defintion overrides
     *  SQLSTT - SQL Statement required
     *
     * If TYPDEFNAM and TYPDEFOVR are supplied, they apply to the objects
     * sent with the statement.  Once the statement is over, the default values
     * sent in the ACCRDB are once again in effect.  If no values are supplied,
     * the values sent in the ACCRDB are used.
     * Objects may follow in one DSS or in several DSS chained together.
     * 
     * @return SQL Statement
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private String parseEXECSQLIMMobjects() throws DRDAProtocolException, SQLException {
        String sqlStmt = null;
        int codePoint;
        DRDAStatement stmt = database.getDefaultStatement();
        do {
            correlationID = reader.readDssHeader();
            while (reader.moreDssData()) {
                codePoint = reader.readLengthAndCodePoint(false);
                switch (codePoint) {
                // optional
                case CodePoint.TYPDEFNAM:
                    setStmtOrDbByteOrder(false, stmt, parseTYPDEFNAM());
                    break;
                // optional
                case CodePoint.TYPDEFOVR:
                    parseTYPDEFOVR(stmt);
                    break;
                // required
                case CodePoint.SQLSTT:
                    sqlStmt = parseEncodedString();
                    if (SanityManager.DEBUG)
                        trace("sqlStmt = " + sqlStmt);
                    break;
                default:
                    invalidCodePoint(codePoint);
                }
            }
        } while (reader.isChainedWithSameID());

        // SQLSTT is required
        if (sqlStmt == null)
            missingCodePoint(CodePoint.SQLSTT);
        return sqlStmt;
    }

    /**
     * Parse EXCSQLSET objects
     * Objects
     *  TYPDEFNAM - Data type definition name - optional
     *  TYPDEFOVR - Type defintion overrides - optional
     *  SQLSTT - SQL Statement - required (a list of at least one)
     *
     * Objects may follow in one DSS or in several DSS chained together.
     * 
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void parseEXCSQLSETobjects() throws DRDAProtocolException, SQLException {

        boolean gotSqlStt = false;
        boolean hadUnrecognizedStmt = false;

        String sqlStmt = null;
        int codePoint;
        DRDAStatement drdaStmt = database.getDefaultStatement();
        drdaStmt.initialize();

        do {
            correlationID = reader.readDssHeader();
            while (reader.moreDssData()) {

                codePoint = reader.readLengthAndCodePoint(false);

                switch (codePoint) {
                // optional
                case CodePoint.TYPDEFNAM:
                    setStmtOrDbByteOrder(false, drdaStmt, parseTYPDEFNAM());
                    break;
                // optional
                case CodePoint.TYPDEFOVR:
                    parseTYPDEFOVR(drdaStmt);
                    break;
                // required
                case CodePoint.SQLSTT:
                    sqlStmt = parseEncodedString();
                    if (sqlStmt != null)
                        // then we have at least one SQL Statement.
                        gotSqlStt = true;

                    if (sqlStmt.startsWith(TIMEOUT_STATEMENT)) {
                        String timeoutString = sqlStmt.substring(TIMEOUT_STATEMENT.length());
                        pendingStatementTimeout = Integer.parseInt(timeoutString);
                        break;
                    }

                    if (canIgnoreStmt(sqlStmt)) {
                        // We _know_ Derby doesn't recognize this
                        // statement; don't bother trying to execute it.
                        // NOTE: at time of writing, this only applies
                        // to "SET CLIENT" commands, and it was decided
                        // that throwing a Warning for these commands
                        // would confuse people, so even though the DDM
                        // spec says to do so, we choose not to (but
                        // only for SET CLIENT cases).  If this changes
                        // at some point in the future, simply remove
                        // the follwing line; we will then throw a
                        // warning.
                        //                     hadUnrecognizedStmt = true;
                        break;
                    }

                    if (SanityManager.DEBUG)
                        trace("sqlStmt = " + sqlStmt);

                    // initialize statement for reuse
                    drdaStmt.initialize();
                    drdaStmt.getStatement().clearWarnings();
                    try {
                        drdaStmt.getStatement().executeUpdate(sqlStmt);
                    } catch (SQLException e) {

                        // if this is a syntax error, then we take it
                        // to mean that the given SET statement is not
                        // recognized; take note (so we can throw a
                        // warning later), but don't interfere otherwise.
                        if (e.getSQLState().equals(SYNTAX_ERR))
                            hadUnrecognizedStmt = true;
                        else
                            // something else; assume it's serious.
                            throw e;
                    }
                    break;
                default:
                    invalidCodePoint(codePoint);
                }
            }

        } while (reader.isChainedWithSameID());

        // SQLSTT is required.
        if (!gotSqlStt)
            missingCodePoint(CodePoint.SQLSTT);

        // Now that we've processed all SET statements (assuming no
        // severe exceptions), check for warnings and, if we had any,
        // note this in the SQLCARD reply object (but DON'T cause the
        // EXCSQLSET statement to fail).
        if (hadUnrecognizedStmt) {
            throw new SQLWarning("One or more SET statements " + "not recognized.", "01000");
        } // end if.

        return;
    }

    private boolean canIgnoreStmt(String stmt) {
        if (stmt.indexOf("SET CLIENT") != -1)
            return true;
        return false;
    }

    /**
     * Write RDBUPDRM
     * Instance variables
     *  SVRCOD - Severity code - Information only - required
     *  RDBNAM - Relational database name -required
     *  SRVDGN - Server Diagnostic Information -optional
     *
     * @exception DRDAProtocolException
     */
    private void writeRDBUPDRM() throws DRDAProtocolException {
        database.RDBUPDRM_sent = true;
        writer.createDssReply();
        writer.startDdm(CodePoint.RDBUPDRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, CodePoint.SVRCOD_INFO);
        writeRDBNAM(database.getDatabaseName());
        writer.endDdmAndDss();
    }

    private String parsePKGNAMCT() throws DRDAProtocolException {
        reader.skipBytes();
        return null;
    }

    /**
     * Parse PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number
     * Instance Variables
     *   NAMESYMDR - database name - not validated
     *   RDBCOLID - RDB Collection Identifier
     *   PKGID - RDB Package Identifier
     *   PKGCNSTKN - RDB Package Consistency Token
     *   PKGSN - RDB Package Section Number
     *
     * @return <code>Pkgnamcsn</code> value
     * @throws DRDAProtocolException
     */
    private Pkgnamcsn parsePKGNAMCSN() throws DRDAProtocolException {
        if (reader.getDdmLength() == CodePoint.PKGNAMCSN_LEN) {
            // This is a scalar object with the following fields
            reader.readString(rdbnam, CodePoint.RDBNAM_LEN, true);
            if (SanityManager.DEBUG)
                trace("rdbnam = " + rdbnam);

            // A check that the rdbnam field corresponds to a database
            // specified in a ACCRDB term.
            // The check is not performed if the client is DNC_CLIENT
            // with version before 10.3.0 because these clients
            // are broken and send incorrect database name
            // if multiple connections to different databases
            // are created

            // This check was added because of DERBY-1434  
            // check the client version first
            if (appRequester.greaterThanOrEqualTo(10, 3, 0)) {
                // check the database name
                if (!rdbnam.toString().equals(database.getDatabaseName()))
                    rdbnamMismatch(CodePoint.PKGNAMCSN);
            }

            reader.readString(rdbcolid, CodePoint.RDBCOLID_LEN, true);
            if (SanityManager.DEBUG)
                trace("rdbcolid = " + rdbcolid);

            reader.readString(pkgid, CodePoint.PKGID_LEN, true);
            if (SanityManager.DEBUG)
                trace("pkgid = " + pkgid);

            // we need to use the same UCS2 encoding, as this can be
            // bounced back to jcc (or keep the byte array)
            reader.readString(pkgcnstkn, CodePoint.PKGCNSTKN_LEN, false);
            if (SanityManager.DEBUG)
                trace("pkgcnstkn = " + pkgcnstkn);

            pkgsn = reader.readNetworkShort();
            if (SanityManager.DEBUG)
                trace("pkgsn = " + pkgsn);
        } else // extended format
        {
            int length = reader.readNetworkShort();
            if (length < CodePoint.RDBNAM_LEN || length > CodePoint.MAX_NAME)
                badObjectLength(CodePoint.RDBNAM);
            reader.readString(rdbnam, length, true);
            if (SanityManager.DEBUG)
                trace("rdbnam = " + rdbnam);

            // A check that the rdbnam field corresponds to a database
            // specified in a ACCRDB term.
            // The check is not performed if the client is DNC_CLIENT
            // with version before 10.3.0 because these clients
            // are broken and send incorrect database name
            // if multiple connections to different databases
            // are created

            // This check was added because of DERBY-1434

            // check the client version first
            if (appRequester.getClientType() != AppRequester.DNC_CLIENT
                    || appRequester.greaterThanOrEqualTo(10, 3, 0)) {
                // check the database name
                if (!rdbnam.toString().equals(database.getDatabaseName()))
                    rdbnamMismatch(CodePoint.PKGNAMCSN);
            }

            //RDBCOLID can be variable length in this format
            length = reader.readNetworkShort();
            reader.readString(rdbcolid, length, true);
            if (SanityManager.DEBUG)
                trace("rdbcolid = " + rdbcolid);

            length = reader.readNetworkShort();
            if (length != CodePoint.PKGID_LEN)
                badObjectLength(CodePoint.PKGID);
            reader.readString(pkgid, CodePoint.PKGID_LEN, true);
            if (SanityManager.DEBUG)
                trace("pkgid = " + pkgid);

            reader.readString(pkgcnstkn, CodePoint.PKGCNSTKN_LEN, false);
            if (SanityManager.DEBUG)
                trace("pkgcnstkn = " + pkgcnstkn);

            pkgsn = reader.readNetworkShort();
            if (SanityManager.DEBUG)
                trace("pkgsn = " + pkgsn);
        }

        // In most cases, the pkgnamcsn object is equal to the
        // previously returned object. To avoid allocation of a new
        // object in these cases, we first check to see if the old
        // object can be reused.
        if ((prevPkgnamcsn == null) || rdbnam.wasModified() || rdbcolid.wasModified() || pkgid.wasModified()
                || pkgcnstkn.wasModified() || (prevPkgnamcsn.getPkgsn() != pkgsn)) {
            // The byte array returned by pkgcnstkn.getBytes() might
            // be modified by DDMReader.readString() later, so we have
            // to create a copy of the array.
            byte[] token = new byte[pkgcnstkn.length()];
            System.arraycopy(pkgcnstkn.getBytes(), 0, token, 0, token.length);

            prevPkgnamcsn = new Pkgnamcsn(rdbnam.toString(), rdbcolid.toString(), pkgid.toString(), pkgsn,
                    new ConsistencyToken(token));
        }

        return prevPkgnamcsn;
    }

    /**
     * Parse SQLSTT Dss
     * @exception DRDAProtocolException
     */
    private String parseSQLSTTDss() throws DRDAProtocolException {
        correlationID = reader.readDssHeader();
        int codePoint = reader.readLengthAndCodePoint(false);
        String strVal = parseEncodedString();
        if (SanityManager.DEBUG)
            trace("SQL Statement = " + strVal);
        return strVal;
    }

    /**
     * Parse an encoded data string from the Application Requester
     *
     * @return string value
     * @exception DRDAProtocolException
     */
    private String parseEncodedString() throws DRDAProtocolException {
        if (sqlamLevel < 7)
            return parseVCMorVCS();
        else
            return parseNOCMorNOCS();
    }

    /**
     * Parse variable character mixed byte or variable character single byte
     * Format
     *  I2 - VCM Length
     *  N bytes - VCM value
     *  I2 - VCS Length
     *  N bytes - VCS value 
     * Only 1 of VCM length or VCS length can be non-zero
     *
     * @return string value
     */
    private String parseVCMorVCS() throws DRDAProtocolException {
        String strVal = null;
        int vcm_length = reader.readNetworkShort();
        if (vcm_length > 0)
            strVal = parseCcsidMBC(vcm_length);
        int vcs_length = reader.readNetworkShort();
        if (vcs_length > 0) {
            if (strVal != null)
                agentError("Both VCM and VCS have lengths > 0");
            strVal = parseCcsidSBC(vcs_length);
        }
        return strVal;
    }

    /**
     * Parse nullable character mixed byte or nullable character single byte
     * Format
     *  1 byte - null indicator
     *  I4 - mixed character length
     *  N bytes - mixed character string
     *  1 byte - null indicator
     *  I4 - single character length
     *  N bytes - single character length string
     *
     * @return string value
     * @exception DRDAProtocolException
     */
    private String parseNOCMorNOCS() throws DRDAProtocolException {
        byte nocm_nullByte = reader.readByte();
        String strVal = null;
        int length;
        if (nocm_nullByte != NULL_VALUE) {
            length = reader.readNetworkInt();
            strVal = parseCcsidMBC(length);
        }
        byte nocs_nullByte = reader.readByte();
        if (nocs_nullByte != NULL_VALUE) {
            if (strVal != null)
                agentError("Both CM and CS are non null");
            length = reader.readNetworkInt();
            strVal = parseCcsidSBC(length);
        }
        return strVal;
    }

    /**
     * Parse mixed character string
     * 
     * @return string value
     * @exception DRDAProtocolException
     */
    private String parseCcsidMBC(int length) throws DRDAProtocolException {
        String strVal = null;
        DRDAStatement currentStatement;

        currentStatement = database.getCurrentStatement();
        if (currentStatement == null) {
            currentStatement = database.getDefaultStatement();
            currentStatement.initialize();
        }
        String ccsidMBCEncoding = currentStatement.ccsidMBCEncoding;

        if (length == 0)
            return null;
        byte[] byteStr = reader.readBytes(length);
        if (ccsidMBCEncoding != null) {
            try {
                strVal = new String(byteStr, 0, length, ccsidMBCEncoding);
            } catch (UnsupportedEncodingException e) {
                agentError("Unsupported encoding " + ccsidMBCEncoding + "in parseCcsidMBC");
            }
        } else
            agentError("Attempt to decode mixed byte string without CCSID being set");
        return strVal;
    }

    /**
     * Parse single byte character string
     * 
     * @return string value
     * @exception DRDAProtocolException
     */
    private String parseCcsidSBC(int length) throws DRDAProtocolException {
        String strVal = null;
        DRDAStatement currentStatement;

        currentStatement = database.getCurrentStatement();
        if (currentStatement == null) {
            currentStatement = database.getDefaultStatement();
            currentStatement.initialize();
        }
        String ccsidSBCEncoding = currentStatement.ccsidSBCEncoding;
        System.out.println("ccsidSBCEncoding - " + ccsidSBCEncoding);

        if (length == 0)
            return null;
        byte[] byteStr = reader.readBytes(length);
        if (ccsidSBCEncoding != null) {
            try {
                strVal = new String(byteStr, 0, length, ccsidSBCEncoding);
            } catch (UnsupportedEncodingException e) {
                agentError("Unsupported encoding " + ccsidSBCEncoding + "in parseCcsidSBC");
            }
        } else
            agentError("Attempt to decode single byte string without CCSID being set");
        return strVal;
    }

    /**
     * Parse CLSQRY
     * Instance Variables
     *  RDBNAM - relational database name - optional
     *  PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
     *  QRYINSID - Query Instance Identifier - required - level 7
     *  MONITOR - Monitor events - optional.
     *
     * @return DRDAstatement being closed
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private DRDAStatement parseCLSQRY() throws DRDAProtocolException, SQLException {
        Pkgnamcsn pkgnamcsn = null;
        reader.markCollection();
        long qryinsid = 0;
        boolean gotQryinsid = false;

        int codePoint = reader.getCodePoint();
        while (codePoint != -1) {
            switch (codePoint) {
            // optional
            case CodePoint.RDBNAM:
                setDatabase(CodePoint.CLSQRY);
                break;
            // required
            case CodePoint.PKGNAMCSN:
                pkgnamcsn = parsePKGNAMCSN();
                break;
            case CodePoint.QRYINSID:
                qryinsid = reader.readNetworkLong();
                gotQryinsid = true;
                break;
            // optional
            case CodePoint.MONITOR:
                parseMONITOR();
                break;
            default:
                invalidCodePoint(codePoint);
            }
            codePoint = reader.getCodePoint();
        }
        // check for required variables
        if (pkgnamcsn == null)
            missingCodePoint(CodePoint.PKGNAMCSN);
        if (sqlamLevel >= MGRLVL_7 && !gotQryinsid)
            missingCodePoint(CodePoint.QRYINSID);

        DRDAStatement stmt = database.getDRDAStatement(pkgnamcsn);
        if (stmt == null) {
            //XXX should really throw a SQL Exception here
            invalidValue(CodePoint.PKGNAMCSN);
        }

        if (stmt.wasExplicitlyClosed()) {
            // JCC still sends a CLSQRY even though we have
            // implicitly closed the resultSet.
            // Then complains if we send the writeQRYNOPRM
            // So for now don't send it
            // Also metadata calls seem to get bound to the same
            // PGKNAMCSN, so even for explicit closes we have
            // to ignore.
            //writeQRYNOPRM(CodePoint.SVRCOD_ERROR);
            pkgnamcsn = null;
        }

        stmt.CLSQRY();

        return stmt;
    }

    /**
     * Parse MONITOR
     * DRDA spec says this is optional.  Since we
     * don't currently support it, we just ignore.
     */
    private void parseMONITOR() throws DRDAProtocolException {

        // Just ignore it.
        reader.skipBytes();
        return;

    }

    private void writeSQLCARDs(SQLException e, int updateCount) throws DRDAProtocolException {
        writeSQLCARDs(e, updateCount, false);
    }

    private void writeSQLCARDs(SQLException e, int updateCount, boolean sendSQLERRRM) throws DRDAProtocolException {

        int severity = CodePoint.SVRCOD_INFO;
        if (e == null) {
            writeSQLCARD(e, severity, updateCount, 0);
            return;
        }

        // instead of writing a chain of sql error or warning, we send the first one, this is
        // jcc/db2 limitation, see beetle 4629

        // If it is a real SQL Error write a SQLERRRM first
        severity = getExceptionSeverity(e);
        if (severity > CodePoint.SVRCOD_ERROR) {
            // For a session ending error > CodePoint.SRVCOD_ERROR you cannot
            // send a SQLERRRM. A CMDCHKRM is required.  In XA if there is a
            // lock timeout it ends the whole session. I am not sure this 
            // is the correct behaviour but if it occurs we have to send 
            // a CMDCHKRM instead of SQLERRM
            writeCMDCHKRM(severity);
        } else if (sendSQLERRRM) {
            writeSQLERRRM(severity);
        }
        writeSQLCARD(e, severity, updateCount, 0);
    }

    private int getSqlCode(int severity) {
        if (severity == CodePoint.SVRCOD_WARNING) // warning
            return 100; //CLI likes it
        else if (severity == CodePoint.SVRCOD_INFO)
            return 0;
        else
            return -1;
    }

    private void writeSQLCARD(SQLException e, int severity, int updateCount, long rowCount)
            throws DRDAProtocolException {
        writer.createDssObject();
        writer.startDdm(CodePoint.SQLCARD);
        writeSQLCAGRP(e, updateCount, rowCount);
        writer.endDdmAndDss();

        // If we have a shutdown exception, restart the server.
        if (e != null) {
            String sqlState = e.getSQLState();
            if (sqlState.regionMatches(0, SQLState.CLOUDSCAPE_SYSTEM_SHUTDOWN, 0, 5)) {
                // then we're here because of a shutdown exception;
                // "clean up" by restarting the server.
                try {
                    server.startNetworkServer();
                } catch (Exception restart)
                // any error messages should have already been printed,
                // so we ignore this exception here.
                {
                }
            }
        }

    }

    /**
     * Write a null SQLCARD as an object
     *
      * @exception DRDAProtocolException
     */
    private void writeNullSQLCARDobject() throws DRDAProtocolException {
        writer.createDssObject();
        writer.startDdm(CodePoint.SQLCARD);
        writeSQLCAGRP(nullSQLState, 0, 0, 0);
        writer.endDdmAndDss();
    }

    /**
     * Write SQLERRRM
     *
     * Instance Variables
     *    SVRCOD - Severity Code - required
      *
     * @param   severity   severity of error
     *
     * @exception DRDAProtocolException
     */
    private void writeSQLERRRM(int severity) throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.SQLERRRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, severity);
        writer.endDdmAndDss();

    }

    /**
     * Write CMDCHKRM
     *
     * Instance Variables
     *    SVRCOD - Severity Code - required 
      *
     * @param   severity   severity of error
     *
     * @exception DRDAProtocolException
     */
    private void writeCMDCHKRM(int severity) throws DRDAProtocolException {
        writer.createDssReply();
        writer.startDdm(CodePoint.CMDCHKRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, severity);
        writer.endDdmAndDss();

    }

    /**
     * Translate from Derby exception severity to SVRCOD
     *
     * @param e SQLException
     */
    private int getExceptionSeverity(SQLException e) {
        int severity = CodePoint.SVRCOD_INFO;

        if (e == null)
            return severity;

        int ec = e.getErrorCode();
        switch (ec) {
        case ExceptionSeverity.STATEMENT_SEVERITY:
        case ExceptionSeverity.TRANSACTION_SEVERITY:
            severity = CodePoint.SVRCOD_ERROR;
            break;
        case ExceptionSeverity.WARNING_SEVERITY:
            severity = CodePoint.SVRCOD_WARNING;
            break;
        case ExceptionSeverity.SESSION_SEVERITY:
        case ExceptionSeverity.DATABASE_SEVERITY:
        case ExceptionSeverity.SYSTEM_SEVERITY:
            severity = CodePoint.SVRCOD_SESDMG;
            break;
        default:
            String sqlState = e.getSQLState();
            if (sqlState != null && sqlState.startsWith("01")) // warning
                severity = CodePoint.SVRCOD_WARNING;
            else
                severity = CodePoint.SVRCOD_ERROR;
        }

        return severity;

    }

    /**
     * Write SQLCAGRP
     *
     * SQLCAGRP : FDOCA EARLY GROUP
     * SQL Communcations Area Group Description
     *
     * FORMAT FOR SQLAM <= 6
     *   SQLCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLSTATE; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
     *   SQLERRPROC; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
     *   SQLCAXGRP; DRDA TYPE N-GDA; ENVLID 0x52; Length Override 0
     *
     * FORMAT FOR SQLAM >= 7
     *   SQLCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLSTATE; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
     *   SQLERRPROC; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
     *   SQLCAXGRP; DRDA TYPE N-GDA; ENVLID 0x52; Length Override 0
     *   SQLDIAGGRP; DRDA TYPE N-GDA; ENVLID 0x56; Length Override 0
     *
     * @param e    SQLException encountered
     * 
     * @exception DRDAProtocolException
     */
    private void writeSQLCAGRP(SQLException e, int updateCount, long rowCount) throws DRDAProtocolException {
        int sqlcode = 0;

        if (e == null) {
            // Forwarding to the optimized version when there is no
            // exception object
            writeSQLCAGRP(nullSQLState, sqlcode, updateCount, rowCount);
            return;
        }

        // SQLWarnings should have warning severity, except if it's a
        // DataTruncation warning for write operations (with SQLState 22001),
        // which is supposed to be used as an exception even though it's a
        // sub-class of SQLWarning.
        if (e instanceof SQLWarning && !SQLState.LANG_STRING_TRUNCATION.equals(e.getSQLState())) {
            sqlcode = ExceptionSeverity.WARNING_SEVERITY;
        } else {
            // Get the SQLCODE for exceptions. Note that this call will always
            // return -1, so the real error code will be lost.
            sqlcode = getSqlCode(getExceptionSeverity(e));
        }

        if (rowCount < 0 && updateCount < 0) {
            writer.writeByte(CodePoint.NULLDATA);
            return;
        }

        if (SanityManager.DEBUG && server.debugOutput && sqlcode < 0) {
            trace("handle SQLException here");
            trace("reason is: " + e.getMessage());
            trace("SQLState is: " + e.getSQLState());
            trace("vendorCode is: " + e.getErrorCode());
            trace("nextException is: " + e.getNextException());
            server.consoleExceptionPrint(e);
            trace("wrapping SQLException into SQLCARD...");
        }

        //null indicator
        writer.writeByte(0);

        // SQLCODE
        writer.writeInt(sqlcode);

        // SQLSTATE
        writer.writeString(e.getSQLState());

        // SQLERRPROC
        // Write the byte[] constant rather than the string, for efficiency
        writer.writeBytes(server.prdIdBytes_);

        // SQLCAXGRP
        writeSQLCAXGRP(updateCount, rowCount, buildSqlerrmc(e), e.getNextException());
    }

    /**
     * Same as writeSQLCAGRP, but optimized for the case
     * when there is no real exception, i.e. the exception is null, or "End
     * of data"
     *
     * SQLCAGRP : FDOCA EARLY GROUP
     * SQL Communcations Area Group Description
     *
     * FORMAT FOR SQLAM <= 6
     *   SQLCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLSTATE; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
     *   SQLERRPROC; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
     *   SQLCAXGRP; DRDA TYPE N-GDA; ENVLID 0x52; Length Override 0
     *
     * FORMAT FOR SQLAM >= 7
     *   SQLCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLSTATE; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
     *   SQLERRPROC; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
     *   SQLCAXGRP; DRDA TYPE N-GDA; ENVLID 0x52; Length Override 0
     *   SQLDIAGGRP; DRDA TYPE N-GDA; ENVLID 0x56; Length Override 0
     *
     * @param sqlState     SQLState (already converted to UTF8)
     * @param sqlcode    sqlcode
     * @param updateCount
     * @param rowCount
     * 
     * @exception DRDAProtocolException
     */

    private void writeSQLCAGRP(byte[] sqlState, int sqlcode, int updateCount, long rowCount)
            throws DRDAProtocolException {
        if (rowCount < 0 && updateCount < 0) {
            writer.writeByte(CodePoint.NULLDATA);
            return;
        }

        //null indicator
        writer.writeByte(0);

        // SQLCODE
        writer.writeInt(sqlcode);

        // SQLSTATE
        writer.writeBytes(sqlState);

        // SQLERRPROC
        writer.writeBytes(server.prdIdBytes_);

        // SQLCAXGRP (Uses null as sqlerrmc since there is no error)
        writeSQLCAXGRP(updateCount, rowCount, null, null);
    }

    // Delimiters for SQLERRMC values.
    // The token delimiter value will be used to parse the MessageId from the 
    // SQLERRMC in MessageService.getLocalizedMessage and the MessageId will be
    // used to retrive the localized message. If this delimiter value is changed
    // please make sure to make appropriate changes in
    // MessageService.getLocalizedMessage that gets called from 
    // SystemProcedures.SQLCAMESSAGE
    /**
     * <code>SQLERRMC_TOKEN_DELIMITER</code> separates message argument tokens 
     */
    private static String SQLERRMC_TOKEN_DELIMITER = new String(new char[] { (char) 20 });

    /**
     * <code>SQLERRMC_PREFORMATTED_MESSAGE_DELIMITER</code>, When full message text is 
     * sent for severe errors. This value separates the messages. 
     */
    private static String SQLERRMC_PREFORMATTED_MESSAGE_DELIMITER = "::";

    /**
     * Create error message or message argements to return to client.  
     * The SQLERRMC will normally be passed back  to the server in a call 
     * to the SYSIBM.SQLCAMESSAGE but for severe exceptions the stored procedure 
     * call cannot be made. So for Severe messages we will just send the message text.
     * 
     * This method will also truncate the value according the client capacity.
     * CCC can only handle 70 characters.
     * 
      * Server sends the sqlerrmc using UTF8 encoding to the client.
      * To get the message, client sends back information to the server
      * calling SYSIBM.SQLCAMESSAGE (see Sqlca.getMessage).  Several parameters 
      * are sent to this procedure including the locale, the sqlerrmc that the 
      * client received from the server. 
      * On server side, the procedure SQLCAMESSAGE in SystemProcedures then calls
      * the MessageService.getLocalizedMessage to retrieve the localized error message. 
      * In MessageService.getLocalizedMessage the sqlerrmc that is passed in, 
      * is parsed to retrieve the message id. The value it uses to parse the MessageId
      * is char value of 20, otherwise it uses the entire sqlerrmc as the message id. 
      * This messageId is then used to retrieve the localized message if present, to 
      * the client.
      * 
     * @param se  SQLException to build SQLERRMC
     *  
     * @return  String which is either the message arguments to be passed to 
     *          SYSIBM.SQLCAMESSAGE or just message text for severe errors.  
     */
    private String buildSqlerrmc(SQLException se) {
        boolean severe = (se.getErrorCode() >= ExceptionSeverity.SESSION_SEVERITY);
        String sqlerrmc = null;

        // get exception which carries Derby messageID and args, per DERBY-1178
        se = Util.getExceptionFactory().getArgumentFerry(se);

        if (se instanceof EmbedSQLException && !severe)
            sqlerrmc = buildTokenizedSqlerrmc(se);
        else if (se instanceof DataTruncation)
            sqlerrmc = buildDataTruncationSqlerrmc((DataTruncation) se);
        else {
            // If this is not an EmbedSQLException or is a severe excecption where
            // we have no hope of succussfully calling the SYSIBM.SQLCAMESSAGE send
            // preformatted message using the server locale
            sqlerrmc = buildPreformattedSqlerrmc(se);
        }
        // Truncate the sqlerrmc to a length that the client can support.
        int maxlen = (sqlerrmc == null) ? -1
                : Math.min(sqlerrmc.length(), appRequester.supportedMessageParamLength());
        if ((maxlen >= 0) && (sqlerrmc.length() > maxlen))
            // have to truncate so the client can handle it.
            sqlerrmc = sqlerrmc.substring(0, maxlen);
        return sqlerrmc;
    }

    /**
     * Build preformatted SQLException text 
     * for severe exceptions or SQLExceptions that are not EmbedSQLExceptions.
     * Just send the message text localized to the server locale.
     * 
     * @param se  SQLException for which to build SQLERRMC
     * @return preformated message text 
     *          with messages separted by SQLERRMC_PREFORMATED_MESSAGE_DELIMITER
     * 
     */
    private String buildPreformattedSqlerrmc(SQLException se) {
        if (se == null)
            return "";

        StringBuffer sb = new StringBuffer();
        // String buffer to build up message
        do {
            sb.append(se.getLocalizedMessage());
            se = se.getNextException();
            if (se != null)
                sb.append(SQLERRMC_PREFORMATTED_MESSAGE_DELIMITER + "SQLSTATE: " + se.getSQLState());
        } while (se != null);
        return sb.toString();
    }

    /**
     * Build Tokenized SQLERRMC to just send the tokenized arguments to the client.
     * for a Derby SQLException or an SQLException thrown by user code.
     * Message argument tokens are separated by SQLERRMC_TOKEN_DELIMITER 
     * Multiple messages are separated by SystemProcedures.SQLERRMC_MESSAGE_DELIMITER
     * 
     *                 ...
     * @param se   SQLException to print
     * 
     */
    private String buildTokenizedSqlerrmc(SQLException se) {

        String sqlerrmc = "";
        do {
            if (se instanceof EmbedSQLException) {
                String messageId = ((EmbedSQLException) se).getMessageId();
                // arguments are variable part of a message
                Object[] args = ((EmbedSQLException) se).getArguments();
                for (int i = 0; args != null && i < args.length; i++)
                    sqlerrmc += args[i] + SQLERRMC_TOKEN_DELIMITER;
                sqlerrmc += messageId;
                se = se.getNextException();
            } else {
                // this could happen for instance if an SQLException was thrown
                // from a stored procedure.
                StringBuffer sb = new StringBuffer();
                sb.append(se.getLocalizedMessage());
                se = se.getNextException();
                if (se != null)
                    sb.append(SQLERRMC_TOKEN_DELIMITER + "SQLSTATE: " + se.getSQLState());
                sqlerrmc += sb.toString();
            }
            if (se != null) {
                sqlerrmc += SystemProcedures.SQLERRMC_MESSAGE_DELIMITER + se.getSQLState() + ":";
            }
        } while (se != null);
        return sqlerrmc;
    }

    /**
     * Build the SQLERRMC for a {@code java.sql.DataTruncation} warning.
     * Serialize all the fields of the {@code DataTruncation} instance in the
     * order in which they appear in the parameter list of the constructor.
     *
     * @param dt the {@code DataTruncation} instance to serialize
     * @return the SQLERRMC string with all fields of the warning
     */
    private String buildDataTruncationSqlerrmc(DataTruncation dt) {
        return dt.getIndex() + SQLERRMC_TOKEN_DELIMITER + dt.getParameter() + SQLERRMC_TOKEN_DELIMITER
                + dt.getRead() + SQLERRMC_TOKEN_DELIMITER + dt.getDataSize() + SQLERRMC_TOKEN_DELIMITER
                + dt.getTransferSize();
    }

    /**
     * Write SQLCAXGRP
     *
     * SQLCAXGRP : EARLY FDOCA GROUP
     * SQL Communications Area Exceptions Group Description
     *
     * FORMAT FOR SQLAM <= 6
     *   SQLRDBNME; DRDA TYPE FCS; ENVLID 0x30; Length Override 18
     *   SQLERRD1; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD2; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD3; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD4; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD5; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD6; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLWARN0; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN1; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN2; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN3; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN4; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN5; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN6; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN7; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN8; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN9; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARNA; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLERRMSG_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 70
     *   SQLERRMSG_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 70
     *
     * FORMAT FOR SQLAM >= 7
     *   SQLERRD1; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD2; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD3; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD4; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD5; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLERRD6; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLWARN0; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN1; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN2; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN3; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN4; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN5; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN6; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN7; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN8; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARN9; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLWARNA; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
     *   SQLRDBNAME; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
     *   SQLERRMSG_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 70
     *   SQLERRMSG_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 70
     * @param nextException SQLException encountered
     * @param sqlerrmc sqlcode
     * 
     * @exception DRDAProtocolException
     */
    private void writeSQLCAXGRP(int updateCount, long rowCount, String sqlerrmc, SQLException nextException)
            throws DRDAProtocolException {
        writer.writeByte(0); // SQLCAXGRP INDICATOR
        if (sqlamLevel < 7) {
            writeRDBNAM(database.getDatabaseName());
            writeSQLCAERRWARN(updateCount, rowCount);
        } else {
            // SQL ERRD1 - D6, WARN0-WARNA (35 bytes)
            writeSQLCAERRWARN(updateCount, rowCount);
            writer.writeShort(0); //CCC on Win does not take RDBNAME
        }
        writeVCMorVCS(sqlerrmc);
        if (sqlamLevel >= 7)
            writeSQLDIAGGRP(nextException);
    }

    /**
     * Write the ERR and WARN part of the SQLCA
     *
     * @param updateCount
     * @param rowCount 
     */
    private void writeSQLCAERRWARN(int updateCount, long rowCount) {
        // SQL ERRD1 - ERRD2 - row Count
        writer.writeInt((int) ((rowCount >>> 32)));
        writer.writeInt((int) (rowCount & 0x0000000ffffffffL));
        // SQL ERRD3 - updateCount
        writer.writeInt(updateCount);
        // SQL ERRD4 - D6 (12 bytes)
        writer.writeBytes(errD4_D6); // byte[] constant
        // WARN0-WARNA (11 bytes)
        writer.writeBytes(warn0_warnA); // byte[] constant
    }

    /**
      * Write SQLDIAGGRP: SQL Diagnostics Group Description - Identity 0xD1
     * Nullable Group
     * SQLDIAGSTT; DRDA TYPE N-GDA; ENVLID 0xD3; Length Override 0
     * SQLDIAGCN;  DRFA TYPE N-RLO; ENVLID 0xF6; Length Override 0
     * SQLDIAGCI;  DRDA TYPE N-RLO; ENVLID 0xF5; Length Override 0
     */
    private void writeSQLDIAGGRP(SQLException nextException) throws DRDAProtocolException {
        // for now we only want to send ROW_DELETED and ROW_UPDATED warnings
        // as extended diagnostics
        // move to first ROW_DELETED or ROW_UPDATED exception. These have been
        // added to the end of the warning chain.
        while (nextException != null && nextException.getSQLState() != SQLState.ROW_UPDATED
                && nextException.getSQLState() != SQLState.ROW_DELETED) {
            nextException = nextException.getNextException();
        }

        if ((nextException == null) || (diagnosticLevel == CodePoint.DIAGLVL0)) {
            writer.writeByte(CodePoint.NULLDATA);
            return;
        }
        writer.writeByte(0); // SQLDIAGGRP indicator

        writeSQLDIAGSTT();
        writeSQLDIAGCI(nextException);
        writeSQLDIAGCN();
    }

    /*
     * writeSQLDIAGSTT: Write NULLDATA for now
     */
    private void writeSQLDIAGSTT() throws DRDAProtocolException {
        writer.writeByte(CodePoint.NULLDATA);
        return;
    }

    /**
     * writeSQLDIAGCI: SQL Diagnostics Condition Information Array - Identity 0xF5
     * SQLNUMROW; ROW LID 0x68; ELEMENT TAKEN 0(all); REP FACTOR 1
     * SQLDCIROW; ROW LID 0xE5; ELEMENT TAKEN 0(all); REP FACTOR 0(all)
     */
    private void writeSQLDIAGCI(SQLException nextException) throws DRDAProtocolException {
        SQLException se = nextException;
        long rowNum = 1;

        /* Write the number of next exceptions to expect */
        writeSQLNUMROW(se);

        while (se != null) {
            String sqlState = se.getSQLState();

            // SQLCode > 0 -> Warning
            // SQLCode = 0 -> Info
            // SQLCode < 0 -> Error
            int severity = getExceptionSeverity(se);
            int sqlCode = -1;
            if (severity == CodePoint.SVRCOD_WARNING)
                sqlCode = 1;
            else if (severity == CodePoint.SVRCOD_INFO)
                sqlCode = 0;

            String sqlerrmc = "";
            if (diagnosticLevel == CodePoint.DIAGLVL1) {
                sqlerrmc = se.getLocalizedMessage();
            }

            // arguments are variable part of a message
            // only send arguments for diagnostic level 0
            if (diagnosticLevel == CodePoint.DIAGLVL0) {
                // we are only able to get arguments of EmbedSQLException
                if (se instanceof EmbedSQLException) {
                    Object[] args = ((EmbedSQLException) se).getArguments();
                    for (int i = 0; args != null && i < args.length; i++)
                        sqlerrmc += args[i].toString() + SQLERRMC_TOKEN_DELIMITER;
                }
            }

            String dbname = null;
            if (database != null)
                dbname = database.getDatabaseName();

            writeSQLDCROW(rowNum++, sqlCode, sqlState, dbname, sqlerrmc);

            se = se.getNextException();
        }

        return;
    }

    /**
     * writeSQLNUMROW: Writes SQLNUMROW : FDOCA EARLY ROW
     * SQL Number of Elements Row Description
     * FORMAT FOR SQLAM LEVELS
     * SQLNUMGRP; GROUP LID 0x58; ELEMENT TAKEN 0(all); REP FACTOR 1
     */
    private void writeSQLNUMROW(SQLException nextException) throws DRDAProtocolException {
        writeSQLNUMGRP(nextException);
    }

    /**
     * writeSQLNUMGRP: Writes SQLNUMGRP : FDOCA EARLY GROUP
     * SQL Number of Elements Group Description
     * FORMAT FOR ALL SQLAM LEVELS
     * SQLNUM; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     */
    private void writeSQLNUMGRP(SQLException nextException) throws DRDAProtocolException {
        int i = 0;
        SQLException se;

        /* Count the number of chained exceptions to be sent */
        for (se = nextException; se != null; se = se.getNextException())
            i++;
        writer.writeShort(i);
    }

    /**
     * writeSQLDCROW: SQL Diagnostics Condition Row - Identity 0xE5
     * SQLDCGRP; GROUP LID 0xD5; ELEMENT TAKEN 0(all); REP FACTOR 1
     */
    private void writeSQLDCROW(long rowNum, int sqlCode, String sqlState, String dbname, String sqlerrmc)
            throws DRDAProtocolException {
        writeSQLDCGRP(rowNum, sqlCode, sqlState, dbname, sqlerrmc);
    }

    /**
     * writeSQLDCGRP: SQL Diagnostics Condition Group Description
     * 
     * SQLDCCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     * SQLDCSTATE; DRDA TYPE FCS; ENVLID Ox30; Lengeh Override 5
     * SQLDCREASON; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     * SQLDCLINEN; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     * SQLDCROWN; DRDA TYPE FD; ENVLID 0x0E; Lengeh Override 31
     * SQLDCER01; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     * SQLDCER02; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     * SQLDCER03; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     * SQLDCER04; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     * SQLDCPART; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     * SQLDCPPOP; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     * SQLDCMSGID; DRDA TYPE FCS; ENVLID 0x30; Length Override 10
     * SQLDCMDE; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
     * SQLDCPMOD; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
     * SQLDCRDB; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
     * SQLDCTOKS; DRDA TYPE N-RLO; ENVLID 0xF7; Length Override 0
     * SQLDCMSG_m; DRDA TYPE NVMC; ENVLID 0x3F; Length Override 32672
     * SQLDCMSG_S; DRDA TYPE NVCS; ENVLID 0x33; Length Override 32672
     * SQLDCCOLN_m; DRDA TYPE NVCM ; ENVLID 0x3F; Length Override 255
     * SQLDCCOLN_s; DRDA TYPE NVCS; ENVLID 0x33; Length Override 255
     * SQLDCCURN_m; DRDA TYPE NVCM; ENVLID 0x3F; Length Override 255
     * SQLDCCURN_s; DRDA TYPE NVCS; ENVLID 0x33; Length Override 255
     * SQLDCPNAM_m; DRDA TYPE NVCM; ENVLID 0x3F; Length Override 255
     * SQLDCPNAM_s; DRDA TYPE NVCS; ENVLID 0x33; Length Override 255
     * SQLDCXGRP; DRDA TYPE N-GDA; ENVLID 0xD3; Length Override 1
     */
    private void writeSQLDCGRP(long rowNum, int sqlCode, String sqlState, String dbname, String sqlerrmc)
            throws DRDAProtocolException {
        // SQLDCCODE
        writer.writeInt(sqlCode);

        // SQLDCSTATE
        writer.writeString(sqlState);

        writer.writeInt(0); // REASON_CODE
        writer.writeInt(0); // LINE_NUMBER
        writer.writeLong(rowNum); // ROW_NUMBER

        byte[] byteArray = new byte[1];
        writer.writeScalarPaddedBytes(byteArray, 47, (byte) 0);

        writer.writeShort(0); // CCC on Win does not take RDBNAME
        writer.writeByte(CodePoint.NULLDATA); // MESSAGE_TOKENS
        writer.writeLDString(sqlerrmc); // MESSAGE_TEXT

        writeVCMorVCS(null); // COLUMN_NAME
        writeVCMorVCS(null); // PARAMETER_NAME
        writeVCMorVCS(null); // EXTENDED_NAME
        writer.writeByte(CodePoint.NULLDATA); // SQLDCXGRP
    }

    /*
     * writeSQLDIAGCN: Write NULLDATA for now
     */
    private void writeSQLDIAGCN() throws DRDAProtocolException {
        writer.writeByte(CodePoint.NULLDATA);
        return;
    }

    /** 
     * Write SQLDARD
     *
     * SQLDARD : FDOCA EARLY ARRAY
     * SQL Descriptor Area Row Description with SQL Communications Area
     *
     * FORMAT FOR SQLAM <= 6
     *   SQLCARD; ROW LID 0x64; ELEMENT TAKEN 0(all); REP FACTOR 1
     *   SQLNUMROW; ROW LID 0x68; ELEMENT TAKEN 0(all); REP FACTOR 1
     *   SQLDAROW; ROW LID 0x60; ELEMENT TAKEN 0(all); REP FACTOR 0(all)
     *
     * FORMAT FOR SQLAM >= 7
     *   SQLCARD; ROW LID 0x64; ELEMENT TAKEN 0(all); REP FACTOR 1
     *   SQLDHROW; ROW LID 0xE0; ELEMENT TAKEN 0(all); REP FACTOR 1
     *   SQLNUMROW; ROW LID 0x68; ELEMENT TAKEN 0(all); REP FACTOR 1
     *
     * @param stmt   prepared statement
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void writeSQLDARD(DRDAStatement stmt, boolean rtnOutput, SQLException e)
            throws DRDAProtocolException, SQLException {
        PreparedStatement ps = stmt.getPreparedStatement();
        ResultSetMetaData rsmeta = ps.getMetaData();
        ParameterMetaData pmeta = stmt.getParameterMetaData();
        int numElems = 0;
        if (e == null || e instanceof SQLWarning) {
            if (rtnOutput && (rsmeta != null))
                numElems = rsmeta.getColumnCount();
            else if ((!rtnOutput) && (pmeta != null))
                numElems = pmeta.getParameterCount();
        }

        writer.createDssObject();

        // all went well we will just write a null SQLCA
        writer.startDdm(CodePoint.SQLDARD);
        writeSQLCAGRP(e, 0, 0);

        if (sqlamLevel >= MGRLVL_7)
            writeSQLDHROW(ps.getResultSetHoldability());

        //SQLNUMROW
        if (SanityManager.DEBUG)
            trace("num Elements = " + numElems);
        writer.writeShort(numElems);

        for (int i = 0; i < numElems; i++)
            writeSQLDAGRP(rsmeta, pmeta, i, rtnOutput);
        writer.endDdmAndDss();

    }

    /**
     * Write QRYDSC - Query Answer Set Description
     *
     * @param stmt DRDAStatement we are working on
     * @param FDODSConly   simply the FDODSC, without the wrap
     *
     * Instance Variables
     *   SQLDTAGRP - required
     * 
     * Only 84 columns can be sent in a single QRYDSC.  If there are more columns
     * they must be sent in subsequent QRYDSC.
     * If the QRYDSC will not fit into the current block, as many columns as can
     * fit are sent and then the remaining are sent in the following blocks.
     * 
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void writeQRYDSC(DRDAStatement stmt, boolean FDODSConly) throws DRDAProtocolException, SQLException {

        ResultSet rs = null;
        ResultSetMetaData rsmeta = null;
        ParameterMetaData pmeta = null;
        if (!stmt.needsToSendParamData)
            rs = stmt.getResultSet();
        if (rs == null) // this is a CallableStatement, use parameter meta data
            pmeta = stmt.getParameterMetaData();
        else
            rsmeta = rs.getMetaData();

        int numCols = (rsmeta != null ? rsmeta.getColumnCount() : pmeta.getParameterCount());
        int numGroups = 1;
        int colStart = 1;
        int colEnd = numCols;
        int blksize = stmt.getBlksize() > 0 ? stmt.getBlksize() : CodePoint.QRYBLKSZ_MAX;

        // check for remaining space in current query block
        // Need to mod with blksize so remaining doesn't go negative. 4868
        int remaining = blksize - (writer.getDSSLength() % blksize)
                - (3 + FdocaConstants.SQLCADTA_SQLDTARD_RLO_SIZE);

        // calcuate how may columns can be sent in the current query block
        int firstcols = remaining / FdocaConstants.SQLDTAGRP_COL_DSC_SIZE;

        // check if it doesn't all fit into the first block and 
        //   under FdocaConstants.MAX_VARS_IN_NGDA
        if (firstcols < numCols || numCols > FdocaConstants.MAX_VARS_IN_NGDA) {
            // we are limited to FdocaConstants.MAX_VARS_IN_NGDA
            if (firstcols > FdocaConstants.MAX_VARS_IN_NGDA) {
                if (SanityManager.DEBUG)
                    SanityManager.ASSERT(numCols > FdocaConstants.MAX_VARS_IN_NGDA,
                            "Number of columns " + numCols + " is less than MAX_VARS_IN_NGDA");
                numGroups = numCols / FdocaConstants.MAX_VARS_IN_NGDA;
                // some left over
                if (FdocaConstants.MAX_VARS_IN_NGDA * numGroups < numCols)
                    numGroups++;
                colEnd = FdocaConstants.MAX_VARS_IN_NGDA;
            } else {
                colEnd = firstcols;
                numGroups += (numCols - firstcols) / FdocaConstants.MAX_VARS_IN_NGDA;
                if (FdocaConstants.MAX_VARS_IN_NGDA * numGroups < numCols)
                    numGroups++;
            }
        }

        if (!FDODSConly) {
            writer.createDssObject();
            writer.startDdm(CodePoint.QRYDSC);
        }

        for (int i = 0; i < numGroups; i++) {
            writeSQLDTAGRP(stmt, rsmeta, pmeta, colStart, colEnd, (i == 0 ? true : false));
            colStart = colEnd + 1;
            // 4868 - Limit range to MAX_VARS_IN_NGDA (used to have extra col)
            colEnd = colEnd + FdocaConstants.MAX_VARS_IN_NGDA;
            if (colEnd > numCols)
                colEnd = numCols;
        }
        writer.writeBytes(FdocaConstants.SQLCADTA_SQLDTARD_RLO);
        if (!FDODSConly)
            writer.endDdmAndDss();
    }

    /**
     * Write SQLDTAGRP
     * SQLDAGRP : Late FDOCA GROUP
     * SQL Data Value Group Descriptor
     *  LENGTH - length of the SQLDTAGRP
     *  TRIPLET_TYPE - NGDA for first, CPT for following
     *  ID - SQLDTAGRP_LID for first, NULL_LID for following
     *  For each column
     *    DRDA TYPE 
     *     LENGTH OVERRIDE
     *       For numeric/decimal types
     *        PRECISON
     *        SCALE
     *       otherwise
     *        LENGTH or DISPLAY_WIDTH
     *
     * @param stmt      drda statement
     * @param rsmeta   resultset meta data
     * @param pmeta      parameter meta data for CallableStatement
     * @param colStart   starting column for group to send
     * @param colEnd   end column to send
     * @param first      is this the first group
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void writeSQLDTAGRP(DRDAStatement stmt, ResultSetMetaData rsmeta, ParameterMetaData pmeta, int colStart,
            int colEnd, boolean first) throws DRDAProtocolException, SQLException {

        int length = (FdocaConstants.SQLDTAGRP_COL_DSC_SIZE * ((colEnd + 1) - colStart)) + 3;
        writer.writeByte(length);
        if (first) {

            writer.writeByte(FdocaConstants.NGDA_TRIPLET_TYPE);
            writer.writeByte(FdocaConstants.SQLDTAGRP_LID);
        } else {
            //continued
            writer.writeByte(FdocaConstants.CPT_TRIPLET_TYPE);
            writer.writeByte(FdocaConstants.NULL_LID);

        }

        boolean hasRs = (rsmeta != null); //  if don't have result, then we look at parameter meta

        for (int i = colStart; i <= colEnd; i++) {
            boolean nullable = (hasRs ? (rsmeta.isNullable(i) == rsmeta.columnNullable)
                    : (pmeta.isNullable(i) == JDBC30Translation.PARAMETER_NULLABLE));
            int colType = (hasRs ? rsmeta.getColumnType(i) : pmeta.getParameterType(i));
            int[] outlen = { -1 };
            int drdaType = FdocaConstants.mapJdbcTypeToDrdaType(colType, nullable, appRequester, outlen);

            boolean isDecimal = ((drdaType | 1) == DRDAConstants.DRDA_TYPE_NDECIMAL);
            int precision = 0, scale = 0;
            if (hasRs) {
                precision = rsmeta.getPrecision(i);
                scale = rsmeta.getScale(i);
                stmt.setRsDRDAType(i, drdaType);
                stmt.setRsPrecision(i, precision);
                stmt.setRsScale(i, scale);
            }

            else if (isDecimal) {
                if (stmt.isOutputParam(i)) {
                    precision = pmeta.getPrecision(i);
                    scale = pmeta.getScale(i);
                    ((CallableStatement) stmt.ps).registerOutParameter(i, Types.DECIMAL, scale);

                }

            }

            if (SanityManager.DEBUG)
                trace("jdbcType=" + colType + "  \tdrdaType=" + Integer.toHexString(drdaType));

            // Length or precision and scale for decimal values.
            writer.writeByte(drdaType);
            if (isDecimal) {
                writer.writeByte(precision);
                writer.writeByte(scale);
            } else if (outlen[0] != -1)
                writer.writeShort(outlen[0]);
            else if (hasRs)
                writer.writeShort(rsmeta.getColumnDisplaySize(i));
            else
                writer.writeShort(stmt.getParamLen(i));
        }
    }

    /**
     * Holdability passed in as it can represent the holdability of
     * the statement or a specific result set.
     * @param holdability HOLD_CURSORS_OVER_COMMIT or CLOSE_CURSORS_AT_COMMIT
     * @throws DRDAProtocolException
     * @throws SQLException
     */
    private void writeSQLDHROW(int holdability) throws DRDAProtocolException, SQLException {
        if (JVMInfo.JDK_ID < 2) //write null indicator for SQLDHROW because there is no holdability support prior to jdk1.3
        {
            writer.writeByte(CodePoint.NULLDATA);
            return;
        }

        writer.writeByte(0); // SQLDHROW INDICATOR

        //SQLDHOLD
        writer.writeShort(holdability);

        //SQLDRETURN
        writer.writeShort(0);
        //SQLDSCROLL
        writer.writeShort(0);
        //SQLDSENSITIVE
        writer.writeShort(0);
        //SQLDFCODE
        writer.writeShort(0);
        //SQLDKEYTYPE
        writer.writeShort(0);
        //SQLRDBNAME
        writer.writeShort(0); //CCC on Windows somehow does not take any dbname
        //SQLDSCHEMA
        writeVCMorVCS(null);

    }

    /**
     * Write QRYDTA - Query Answer Set Data
     *  Contains some or all of the answer set data resulting from a query
     *  If the client is not using rowset processing, this routine attempts
     *  to pack as much data into the QRYDTA as it can. This may result in
     *  splitting the last row across the block, in which case when the
     *  client calls CNTQRY we will return the remainder of the row.
     *
     *  Splitting a QRYDTA block is expensive, for several reasons:
     *  - extra logic must be run, on both client and server side
     *  - more network round-trips are involved
     *  - the QRYDTA block which contains the continuation of the split
     *    row is generally wasteful, since it contains the remainder of
     *    the split row but no additional rows.
     *  Since splitting is expensive, the server makes some attempt to
     *  avoid it. Currently, the server's algorithm for this is to
     *  compute the length of the current row, and to stop trying to pack
     *  more rows into this buffer if another row of that length would
     *  not fit. However, since rows can vary substantially in length,
     *  this algorithm is often ineffective at preventing splits. For
     *  example, if a short row near the end of the buffer is then
     *  followed by a long row, that long row will be split. It is possible
     *  to improve this algorithm substantially:
     *  - instead of just using the length of the previous row as a guide
     *    for whether to attempt packing another row in, use some sort of
     *    overall average row size computed over multiple rows (e.g., all
     *    the rows we've placed into this QRYDTA block, or all the rows
     *    we've process for this result set)
     *  - when we discover that the next row will not fit, rather than
     *    splitting the row across QRYDTA blocks, if it is relatively
     *    small, we could just hold the entire row in a buffer to place
     *    it entirely into the next QRYDTA block, or reset the result
     *    set cursor back one row to "unread" this row.
     *  - when splitting a row across QRYDTA blocks, we tend to copy
     *    data around multiple times. Careful coding could remove some
     *    of these copies.
     *  However, it is important not to over-complicate this code: it is
     *  better to be correct than to be efficient, and there have been
     *  several bugs in the split logic already.
     *
     * Instance Variables
     *   Byte string
     *
     * @param stmt   DRDA statement we are processing
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void writeQRYDTA(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        boolean getMoreData = true;
        boolean sentExtData = false;
        int startLength = 0;
        writer.createDssObject();

        if (SanityManager.DEBUG)
            trace("Write QRYDTA");
        writer.startDdm(CodePoint.QRYDTA);
        // Check to see if there was leftover data from splitting
        // the previous QRYDTA for this result set. If there was, and
        // if we have now sent all of it, send any EXTDTA for that row
        // and increment the rowCount which we failed to increment in
        // writeFDODTA when we realized the row needed to be split.
        if (processLeftoverQRYDTA(stmt)) {
            if (stmt.getSplitQRYDTA() == null) {
                stmt.rowCount += 1;
                if (stmt.getExtDtaObjects() != null)
                    writeEXTDTA(stmt);
            }
            return;
        }

        while (getMoreData) {
            sentExtData = false;
            getMoreData = writeFDODTA(stmt);

            if (stmt.getExtDtaObjects() != null && stmt.getSplitQRYDTA() == null) {
                writer.endDdmAndDss();
                writeEXTDTA(stmt);
                getMoreData = false;
                sentExtData = true;
            }

            // if we don't have enough room for a row of the 
            // last row's size, don't try to cram it in.
            // It would get split up but it is not very efficient.
            if (getMoreData == true) {
                int endLength = writer.getDSSLength();
                int rowsize = endLength - startLength;
                if ((stmt.getBlksize() - endLength) < rowsize)
                    getMoreData = false;

                startLength = endLength;
            }

        }
        // If we sent extDta we will rely on
        // writeScalarStream to end the dss with the proper chaining.
        // otherwise end it here.
        if (!sentExtData)
            writer.endDdmAndDss();

        if (!stmt.hasdata()) {
            final boolean qryclsOnLmtblkprc = appRequester.supportsQryclsimpForLmtblkprc();
            if (stmt.isRSCloseImplicit(qryclsOnLmtblkprc)) {
                stmt.rsClose();
            }
        }
    }

    /**
     * This routine places some data into the current QRYDTA block using
     * FDODTA (Formatted Data Object DaTA rules).
     *
     * There are 3 basic types of processing flow for this routine:
     * - In normal non-rowset, non-scrollable cursor flow, this routine
     *   places a single row into the QRYDTA block and returns TRUE,
     *   indicating that the caller can call us back to place another
     *   row into the result set if he wishes. (The caller may need to
     *   send Externalized Data, which would be a reason for him NOT to
     *   place any more rows into the QRYDTA).
     * - In ROWSET processing, this routine places an entire ROWSET of
     *   rows into the QRYDTA block and returns FALSE, indicating that
     *   the QRYDTA block is full and should now be sent.
     * - In callable statement processing, this routine places the
     *   results from the output parameters of the called procedure into
     *   the QRYDTA block. This code path is really dramatically
     *   different from the other two paths and shares only a very small
     *   amount of common code in this routine.
     *
     * In all cases, it is possible that the data we wish to return may
     * not fit into the QRYDTA block, in which case we call splitQRYDTA
     * to split the data and remember the remainder data in the result set.
     * Splitting the data is relatively rare in the normal cursor case,
     * because our caller (writeQRYDTA) uses a coarse estimation
     * technique to avoid calling us if he thinks a split is likely.
     *
     * The overall structure of this routine is implemented as two
     * loops:
     * - the outer "do ... while ... " loop processes a ROWSET, one row
     *   at a time. For non-ROWSET cursors, and for callable statements,
     *   this loop executes only once.
     * - the inner "for ... i < numCols ..." loop processes each column
     *   in the current row, or each output parmeter in the procedure.
     *
     * Most column data is written directly inline in the QRYDTA block.
     * Some data, however, is written as Externalized Data. This is
     * commonly used for Large Objects. In that case, an Externalized
     * Data Pointer is written into the QRYDTA block, and the actual
     * data flows in separate EXTDTA blocks which are returned
     * after this QRYDTA block.
     */
    private boolean writeFDODTA(DRDAStatement stmt) throws DRDAProtocolException, SQLException {
        boolean hasdata = false;
        int blksize = stmt.getBlksize() > 0 ? stmt.getBlksize() : CodePoint.QRYBLKSZ_MAX;
        long rowCount = 0;
        ResultSet rs = null;
        boolean moreData = (stmt.getQryprctyp() == CodePoint.LMTBLKPRC);
        int numCols;

        if (!stmt.needsToSendParamData) {
            rs = stmt.getResultSet();
        }

        if (rs != null) {
            numCols = stmt.getNumRsCols();
            if (stmt.isScrollable())
                hasdata = positionCursor(stmt, rs);
            else
                hasdata = rs.next();
        } else // it's for a CallableStatement
        {
            hasdata = stmt.hasOutputParams();
            numCols = stmt.getDrdaParamCount();
        }

        do {
            if (!hasdata) {
                doneData(stmt, rs);
                moreData = false;
                return moreData;
            }

            // Send ResultSet warnings if there are any
            SQLWarning sqlw = (rs != null) ? rs.getWarnings() : null;
            if (rs != null) {
                rs.clearWarnings();
            }

            // for updatable, insensitive result sets we signal the
            // row updated condition to the client via a warning to be 
            // popped by client onto its rowUpdated state, i.e. this 
            // warning should not reach API level.
            if (rs != null && rs.rowUpdated()) {
                SQLWarning w = new SQLWarning("", SQLState.ROW_UPDATED, ExceptionSeverity.WARNING_SEVERITY);
                if (sqlw != null) {
                    sqlw.setNextWarning(w);
                } else {
                    sqlw = w;
                }
            }
            // Delete holes are manifest as a row consisting of a non-null
            // SQLCARD and a null data group. The SQLCARD has a warning
            // SQLSTATE of 02502
            if (rs != null && rs.rowDeleted()) {
                SQLWarning w = new SQLWarning("", SQLState.ROW_DELETED, ExceptionSeverity.WARNING_SEVERITY);
                if (sqlw != null) {
                    sqlw.setNextWarning(w);
                } else {
                    sqlw = w;
                }
            }

            // Save the position where we start writing the warnings in case
            // we need to add more warnings later.
            final int sqlcagrpStart = writer.getBufferPosition();

            if (sqlw == null)
                writeSQLCAGRP(nullSQLState, 0, -1, -1);
            else
                writeSQLCAGRP(sqlw, 1, -1);

            // Save the position right after the warnings so we know where to
            // insert more warnings later.
            final int sqlcagrpEnd = writer.getBufferPosition();

            // if we were asked not to return data, mark QRYDTA null; do not
            // return yet, need to make rowCount right
            // if the row has been deleted return QRYDTA null (delete hole)
            boolean noRetrieveRS = (rs != null && (!stmt.getQryrtndta() || rs.rowDeleted()));
            if (noRetrieveRS)
                writer.writeByte(0xFF); //QRYDTA null indicator: IS NULL
            else
                writer.writeByte(0); //QRYDTA null indicator: not null

            for (int i = 1; i <= numCols; i++) {
                if (noRetrieveRS)
                    break;

                int drdaType;
                int ndrdaType;
                int precision;
                int scale;

                Object val = null;
                boolean valNull;
                if (rs != null) {
                    drdaType = stmt.getRsDRDAType(i) & 0xff;
                    precision = stmt.getRsPrecision(i);
                    scale = stmt.getRsScale(i);
                    ndrdaType = drdaType | 1;

                    if (SanityManager.DEBUG)
                        trace("!!drdaType = " + java.lang.Integer.toHexString(drdaType) + " precision=" + precision
                                + " scale = " + scale);
                    switch (ndrdaType) {
                    case DRDAConstants.DRDA_TYPE_NLOBBYTES:
                    case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
                        EXTDTAInputStream extdtaStream = EXTDTAInputStream.getEXTDTAStream(rs, i, drdaType);
                        writeFdocaVal(i, extdtaStream, drdaType, precision, scale, extdtaStream.isNull(), stmt,
                                false);
                        break;
                    case DRDAConstants.DRDA_TYPE_NINTEGER:
                        int ival = rs.getInt(i);
                        valNull = rs.wasNull();
                        if (SanityManager.DEBUG)
                            trace("====== writing int: " + ival + " is null: " + valNull);
                        writeNullability(drdaType, valNull);
                        if (!valNull)
                            writer.writeInt(ival);
                        break;
                    case DRDAConstants.DRDA_TYPE_NSMALL:
                        short sval = rs.getShort(i);
                        valNull = rs.wasNull();
                        if (SanityManager.DEBUG)
                            trace("====== writing small: " + sval + " is null: " + valNull);
                        writeNullability(drdaType, valNull);
                        if (!valNull)
                            writer.writeShort(sval);
                        break;
                    case DRDAConstants.DRDA_TYPE_NINTEGER8:
                        long lval = rs.getLong(i);
                        valNull = rs.wasNull();
                        if (SanityManager.DEBUG)
                            trace("====== writing long: " + lval + " is null: " + valNull);
                        writeNullability(drdaType, valNull);
                        if (!valNull)
                            writer.writeLong(lval);
                        break;
                    case DRDAConstants.DRDA_TYPE_NFLOAT4:
                        float fval = rs.getFloat(i);
                        valNull = rs.wasNull();
                        if (SanityManager.DEBUG)
                            trace("====== writing float: " + fval + " is null: " + valNull);
                        writeNullability(drdaType, valNull);
                        if (!valNull)
                            writer.writeFloat(fval);
                        break;
                    case DRDAConstants.DRDA_TYPE_NFLOAT8:
                        double dval = rs.getDouble(i);
                        valNull = rs.wasNull();
                        if (SanityManager.DEBUG)
                            trace("====== writing double: " + dval + " is null: " + valNull);
                        writeNullability(drdaType, valNull);
                        if (!valNull)
                            writer.writeDouble(dval);
                        break;
                    case DRDAConstants.DRDA_TYPE_NCHAR:
                    case DRDAConstants.DRDA_TYPE_NVARCHAR:
                    case DRDAConstants.DRDA_TYPE_NVARMIX:
                    case DRDAConstants.DRDA_TYPE_NLONG:
                    case DRDAConstants.DRDA_TYPE_NLONGMIX:
                        String valStr = rs.getString(i);
                        if (SanityManager.DEBUG)
                            trace("====== writing char/varchar/mix :" + valStr + ":");
                        writeFdocaVal(i, valStr, drdaType, precision, scale, rs.wasNull(), stmt, false);
                        break;
                    default:
                        val = getObjectForWriteFdoca(rs, i, drdaType);
                        writeFdocaVal(i, val, drdaType, precision, scale, rs.wasNull(), stmt, false);
                    }
                } else {

                    drdaType = stmt.getParamDRDAType(i) & 0xff;
                    precision = stmt.getParamPrecision(i);
                    scale = stmt.getParamScale(i);

                    if (stmt.isOutputParam(i)) {
                        int[] outlen = new int[1];
                        drdaType = FdocaConstants.mapJdbcTypeToDrdaType(stmt.getOutputParamType(i), true,
                                appRequester, outlen);
                        precision = stmt.getOutputParamPrecision(i);
                        scale = stmt.getOutputParamScale(i);

                        if (SanityManager.DEBUG)
                            trace("***getting Object " + i);
                        val = getObjectForWriteFdoca((CallableStatement) stmt.ps, i, drdaType);
                        valNull = (val == null);
                        writeFdocaVal(i, val, drdaType, precision, scale, valNull, stmt, true);
                    } else
                        writeFdocaVal(i, null, drdaType, precision, scale, true, stmt, true);

                }
            }

            DataTruncation truncated = stmt.getTruncationWarnings();
            if (truncated != null) {
                // Some of the data was truncated, so we need to add a
                // truncation warning. Save a copy of the row data, then move
                // back to the SQLCAGRP section and overwrite it with the new
                // warnings, and finally re-insert the row data after the new
                // SQLCAGRP section.
                byte[] data = writer.getBufferContents(sqlcagrpEnd);
                writer.setBufferPosition(sqlcagrpStart);
                if (sqlw != null) {
                    truncated.setNextWarning(sqlw);
                }
                writeSQLCAGRP(truncated, 1, -1);
                writer.writeBytes(data);
                stmt.clearTruncationWarnings();
            }

            // does all this fit in one QRYDTA
            if (writer.getDSSLength() > blksize) {
                splitQRYDTA(stmt, blksize);
                return false;
            }

            if (rs == null)
                return moreData;

            //get the next row
            rowCount++;
            if (rowCount < stmt.getQryrowset()) {
                hasdata = rs.next();
            }
            /*(1) scrollable we return at most a row set; OR (2) no retrieve data
             */
            else if (stmt.isScrollable() || noRetrieveRS)
                moreData = false;

        } while (hasdata && rowCount < stmt.getQryrowset());

        // add rowCount to statement row count
        // for non scrollable cursors
        if (!stmt.isScrollable())
            stmt.rowCount += rowCount;

        if (!hasdata) {
            doneData(stmt, rs);
            moreData = false;
        }

        if (!stmt.isScrollable())
            stmt.setHasdata(hasdata);
        return moreData;
    }

    /**
     * <p>
     * Get a column value of the specified type from a {@code ResultSet}, in
     * a form suitable for being writted by {@link #writeFdocaVal}. For most
     * types, this means just calling {@code ResultSet.getObject(int)}.
     * </p>
     *
     * <p>
     * The only exception currently is the data types representing dates and
     * times, as they need to be fetched using the same
     * {@code java.util.Calendar} as {@link #writeFdocaVal} uses when writing
     * them (DERBY-4582).
     * </p>
     *
     * <p>
     * <b>Note:</b> Changes made in this method should also be made in the
     * corresponding method for {@code CallableStatement}:
     * {@link #getObjectForWriteFdoca(java.sql.CallableStatement, int, int)}.
     * </p>
     *
     * @param rs the result set to fetch the object from
     * @param index the column index
     * @param drdaType the DRDA type of the object to fetch
     * @return an object with the value of the column
     * @throws if a database error occurs while fetching the column value
     * @see #getObjectForWriteFdoca(java.sql.CallableStatement, int, int)
     */
    private Object getObjectForWriteFdoca(ResultSet rs, int index, int drdaType) throws SQLException {
        // convert to corresponding nullable type to reduce number of cases
        int ndrdaType = drdaType | 1;
        switch (ndrdaType) {
        case DRDAConstants.DRDA_TYPE_NDATE:
            return rs.getDate(index, getGMTCalendar());
        case DRDAConstants.DRDA_TYPE_NTIME:
            return rs.getTime(index, getGMTCalendar());
        case DRDAConstants.DRDA_TYPE_NTIMESTAMP:
            return rs.getTimestamp(index, getGMTCalendar());
        case DRDAConstants.DRDA_TYPE_NARRAY:
            return rs.getArray(index);
        default:
            return rs.getObject(index);
        }
    }

    /**
     * <p>
     * Get the value of an output parameter of the specified type from a
     * {@code CallableStatement}, in a form suitable for being writted by
     * {@link #writeFdocaVal}. For most types, this means just calling
     * {@code CallableStatement.getObject(int)}.
     * </p>
     *
     * <p>
     * This method should behave like the corresponding method for
     * {@code ResultSet}, and changes made to one of these methods, must be
     * reflected in the other method. See
     * {@link #getObjectForWriteFdoca(java.sql.ResultSet, int, int)}
     * for details.
     * </p>
     *
     * @param cs the callable statement to fetch the object from
     * @param index the parameter index
     * @param drdaType the DRDA type of the object to fetch
     * @return an object with the value of the output parameter
     * @throws if a database error occurs while fetching the parameter value
     * @see #getObjectForWriteFdoca(java.sql.ResultSet, int, int)
     */
    private Object getObjectForWriteFdoca(CallableStatement cs, int index, int drdaType) throws SQLException {
        // convert to corresponding nullable type to reduce number of cases
        int ndrdaType = drdaType | 1;
        switch (ndrdaType) {
        case DRDAConstants.DRDA_TYPE_NDATE:
            return cs.getDate(index, getGMTCalendar());
        case DRDAConstants.DRDA_TYPE_NTIME:
            return cs.getTime(index, getGMTCalendar());
        case DRDAConstants.DRDA_TYPE_NTIMESTAMP:
            return cs.getTimestamp(index, getGMTCalendar());
        case DRDAConstants.DRDA_TYPE_NLOBBYTES:
        case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
            return EXTDTAInputStream.getEXTDTAStream(cs, index, drdaType);
        default:
            return cs.getObject(index);
        }
    }

    /**
     * Split QRYDTA into blksize chunks
     *
     * This routine is called if the QRYDTA data will not fit. It writes
     * as much data as it can, then stores the remainder in the result
     * set. At some later point, when the client returns with a CNTQRY,
     * we will call processLeftoverQRYDTA to handle that data.
     *
     * The interaction between DRDAConnThread and DDMWriter is rather
     * complicated here. This routine gets called because DRDAConnThread
     * realizes that it has constructed a QRYDTA message which is too
     * large. At that point, we need to reclaim the "extra" data and
     * hold on to it. To aid us in that processing, DDMWriter provides
     * the routines getDSSLength, copyDSSDataToEnd, and truncateDSS.
     * For some additional detail on this complex sub-protocol, the
     * interested reader should study bug DERBY-491 and 492 at:
     * http://issues.apache.org/jira/browse/DERBY-491 and
     * http://issues.apache.org/jira/browse/DERBY-492
     *
     * @param stmt DRDA statment
     * @param blksize size of query block
     * 
     * @throws SQLException
      * @throws DRDAProtocolException
     */
    private void splitQRYDTA(DRDAStatement stmt, int blksize) throws SQLException, DRDAProtocolException {
        // make copy of extra data
        byte[] temp = writer.copyDSSDataToEnd(blksize);
        // truncate to end of blocksize
        writer.truncateDSS(blksize);
        if (temp.length == 0)
            agentError("LMTBLKPRC violation: splitQRYDTA was " + "called to split a QRYDTA block, but the "
                    + "entire row fit successfully into the " + "current block. Server rowsize computation "
                    + "was probably incorrect (perhaps an off-by-" + "one bug?). QRYDTA blocksize: " + blksize);
        stmt.setSplitQRYDTA(temp);
    }

    /**
     * Process remainder data resulting from a split.
     *
     * This routine is called at the start of building each QRYDTA block.
     * Normally, it observes that there is no remainder data from the
     * previous QRYDTA block, and returns FALSE, indicating that there
     * was nothing to do.
     *
     * However, if it discovers that the previous QRYDTA block was split,
     * then it retrieves the remainder data from the result set, writes
     * as much of it as will fit into the QRYDTA block (hopefully all of
     * it will fit, but the row may be very long), and returns TRUE,
     * indicating that this QRYDTA block has been filled with remainder
     * data and should now be sent immediately.
     */
    private boolean processLeftoverQRYDTA(DRDAStatement stmt) throws SQLException, DRDAProtocolException {
        byte[] leftovers = stmt.getSplitQRYDTA();
        if (leftovers == null)
            return false;
        int blksize = stmt.getBlksize() > 0 ? stmt.getBlksize() : CodePoint.QRYBLKSZ_MAX;
        blksize = blksize - 10; //DSS header + QRYDTA and length
        if (leftovers.length < blksize) {
            writer.writeBytes(leftovers, 0, leftovers.length);
            stmt.setSplitQRYDTA(null);
        } else {
            writer.writeBytes(leftovers, 0, blksize);
            byte[] newLeftovers = new byte[leftovers.length - blksize];
            System.arraycopy(leftovers, blksize + 0, newLeftovers, 0, newLeftovers.length);
            stmt.setSplitQRYDTA(newLeftovers);
        }
        // finish off query block and send
        writer.endDdmAndDss();
        return true;
    }

    /**
     * Done data
     * Send SQLCARD for the end of the data
     * 
     * @param stmt DRDA statement
     * @param rs Result set
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void doneData(DRDAStatement stmt, ResultSet rs) throws DRDAProtocolException, SQLException {
        if (SanityManager.DEBUG)
            trace("*****NO MORE DATA!!");
        int blksize = stmt.getBlksize() > 0 ? stmt.getBlksize() : CodePoint.QRYBLKSZ_MAX;
        if (rs != null) {
            if (stmt.isScrollable()) {
                //keep isAfterLast and isBeforeFirst to be able 
                //to reposition after counting rows
                boolean isAfterLast = rs.isAfterLast();
                boolean isBeforeFirst = rs.isBeforeFirst();

                // for scrollable cursors - calculate the row count
                // since we may not have gone through each row
                rs.last();
                stmt.rowCount = rs.getRow();

                // reposition after last or before first
                if (isAfterLast) {
                    rs.afterLast();
                }
                if (isBeforeFirst) {
                    rs.beforeFirst();
                }
            } else // non-scrollable cursor
            {
                final boolean qryclsOnLmtblkprc = appRequester.supportsQryclsimpForLmtblkprc();
                if (stmt.isRSCloseImplicit(qryclsOnLmtblkprc)) {
                    stmt.rsClose();
                    stmt.rsSuspend();
                }

            }
        }

        // For scrollable cursor's QRYSCRAFT, when we reach here, DRDA spec says sqlstate
        // is 00000, sqlcode is not mentioned.  But DB2 CLI code expects sqlcode to be 0.
        // We return sqlcode 0 in this case, as the DB2 server does.
        boolean isQRYSCRAFT = (stmt.getQryscrorn() == CodePoint.QRYSCRAFT);

        // Using sqlstate 00000 or 02000 for end of data.
        writeSQLCAGRP((isQRYSCRAFT ? eod00000 : eod02000), (isQRYSCRAFT ? 0 : 100), 0, stmt.rowCount);

        writer.writeByte(CodePoint.NULLDATA);
        // does all this fit in one QRYDTA
        if (writer.getDSSLength() > blksize) {
            splitQRYDTA(stmt, blksize);
        }
    }

    /**
     * Position cursor for insensitive scrollable cursors
     *
     * @param stmt   DRDA statement 
     * @param rs   Result set
     */
    private boolean positionCursor(DRDAStatement stmt, ResultSet rs) throws SQLException, DRDAProtocolException {
        boolean retval = false;
        switch (stmt.getQryscrorn()) {
        case CodePoint.QRYSCRREL:
            int rows = (int) stmt.getQryrownbr();
            if ((rs.isAfterLast() && rows > 0) || (rs.isBeforeFirst() && rows < 0)) {
                retval = false;
            } else {
                retval = rs.relative(rows);
            }
            break;
        case CodePoint.QRYSCRABS:
            // JCC uses an absolute value of 0 which is not allowed in JDBC
            // We translate it into beforeFirst which seems to work.
            if (stmt.getQryrownbr() == 0) {
                rs.beforeFirst();
                retval = false;
            } else {
                retval = rs.absolute((int) stmt.getQryrownbr());
            }
            break;
        case CodePoint.QRYSCRAFT:
            rs.afterLast();
            retval = false;
            break;
        case CodePoint.QRYSCRBEF:
            rs.beforeFirst();
            retval = false;
            break;
        default:
            agentError("Invalid value for cursor orientation " + stmt.getQryscrorn());
        }
        return retval;
    }

    /**
     * Write SQLDAGRP
     * SQLDAGRP : EARLY FDOCA GROUP
     * SQL Data Area Group Description
     *
     * FORMAT FOR SQLAM <= 6
     *   SQLPRECISION; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     *   SQLSCALE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     *   SQLLENGTH; DRDA TYPE I4; ENVLID 0x02; Length Override 4
     *   SQLTYPE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     *   SQLCCSID; DRDA TYPE FB; ENVLID 0x26; Length Override 2
     *   SQLNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 30
     *   SQLNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 30
     *   SQLLABEL_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 30
     *   SQLLABEL_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 30
     *   SQLCOMMENTS_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 254
     *   SQLCOMMENTS_m; DRDA TYPE VCS; ENVLID 0x32; Length Override 254
     *
     * FORMAT FOR SQLAM == 6
     *   SQLPRECISION; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     *   SQLSCALE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     *   SQLLENGTH; DRDA TYPE I8; ENVLID 0x16; Length Override 8
     *   SQLTYPE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     *   SQLCCSID; DRDA TYPE FB; ENVLID 0x26; Length Override 2
     *   SQLNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 30
     *   SQLNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 30
     *   SQLLABEL_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 30
     *   SQLLABEL_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 30
     *   SQLCOMMENTS_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 254
     *   SQLCOMMENTS_m; DRDA TYPE VCS; ENVLID 0x32; Length Override 254
     *   SQLUDTGRP; DRDA TYPE N-GDA; ENVLID 0x51; Length Override 0
     *
     * FORMAT FOR SQLAM >= 7
     *   SQLPRECISION; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     *   SQLSCALE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     *   SQLLENGTH; DRDA TYPE I8; ENVLID 0x16; Length Override 8
     *   SQLTYPE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
     *   SQLCCSID; DRDA TYPE FB; ENVLID 0x26; Length Override 2
     *   SQLDOPTGRP; DRDA TYPE N-GDA; ENVLID 0xD2; Length Override 0
     *
     * @param rsmeta   resultset meta data
     * @param pmeta      parameter meta data
     * @param elemNum   column number we are returning (in case of result set), or,
     *               parameter number (in case of parameter)
     * @param rtnOutput   whether this is for a result set   
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void writeSQLDAGRP(ResultSetMetaData rsmeta, ParameterMetaData pmeta, int elemNum, boolean rtnOutput)
            throws DRDAProtocolException, SQLException {
        //jdbc uses offset of 1

        int jdbcElemNum = elemNum + 1;
        // length to be retreived as output parameter
        int[] outlen = { -1 };

        int elemType = rtnOutput ? rsmeta.getColumnType(jdbcElemNum) : pmeta.getParameterType(jdbcElemNum);

        int precision = rtnOutput ? rsmeta.getPrecision(jdbcElemNum) : pmeta.getPrecision(jdbcElemNum);
        if (precision > FdocaConstants.NUMERIC_MAX_PRECISION)
            precision = FdocaConstants.NUMERIC_MAX_PRECISION;

        // 2-byte precision
        writer.writeShort(precision);
        // 2-byte scale
        int scale = (rtnOutput ? rsmeta.getScale(jdbcElemNum) : pmeta.getScale(jdbcElemNum));
        writer.writeShort(scale);

        boolean nullable = rtnOutput ? (rsmeta.isNullable(jdbcElemNum) == ResultSetMetaData.columnNullable)
                : (pmeta.isNullable(jdbcElemNum) == JDBC30Translation.PARAMETER_NULLABLE);

        int sqlType = SQLTypes.mapJdbcTypeToDB2SqlType(elemType, nullable, appRequester, outlen);

        if (outlen[0] == -1) //some types not set
        {
            switch (elemType) {
            case Types.DECIMAL:
            case Types.NUMERIC:
                scale = rtnOutput ? rsmeta.getScale(jdbcElemNum) : pmeta.getScale(jdbcElemNum);
                outlen[0] = ((precision << 8) | (scale << 0));
                if (SanityManager.DEBUG)
                    trace("\n\nprecision =" + precision + " scale =" + scale);
                break;
            default:
                outlen[0] = Math.min(FdocaConstants.LONGVARCHAR_MAX_LEN,
                        (rtnOutput ? rsmeta.getColumnDisplaySize(jdbcElemNum) : pmeta.getPrecision(jdbcElemNum)));
            }
        }

        switch (elemType) {
        case Types.BINARY:
        case Types.VARBINARY:
        case Types.LONGVARBINARY:
        case Types.BLOB: //for CLI describe to be correct
        case Types.CLOB:
            outlen[0] = (rtnOutput ? rsmeta.getPrecision(jdbcElemNum) : pmeta.getPrecision(jdbcElemNum));
        }

        if (SanityManager.DEBUG)
            trace("SQLDAGRP len =" + java.lang.Integer.toHexString(outlen[0]) + "for type:" + elemType);

        // 8 or 4 byte sqllength
        if (sqlamLevel >= MGRLVL_6)
            writer.writeLong(outlen[0]);
        else
            writer.writeInt(outlen[0]);

        String typeName = rtnOutput ? rsmeta.getColumnTypeName(jdbcElemNum)
                : pmeta.getParameterTypeName(jdbcElemNum);
        if (SanityManager.DEBUG)
            trace("jdbcType =" + typeName + "  sqlType =" + sqlType + "len =" + outlen[0]);

        writer.writeShort(sqlType);

        // CCSID
        // CCSID should be 0 for Binary Types.

        if (elemType == java.sql.Types.CHAR || elemType == java.sql.Types.VARCHAR
                || elemType == java.sql.Types.LONGVARCHAR || elemType == java.sql.Types.CLOB)
            writer.writeScalar2Bytes(1208);
        else
            writer.writeScalar2Bytes(0);

        if (sqlamLevel < MGRLVL_7) {

            //SQLName
            writeVCMorVCS(rtnOutput ? rsmeta.getColumnName(jdbcElemNum) : null);
            //SQLLabel
            writeVCMorVCS(null);
            //SQLComments
            writeVCMorVCS(null);

            if (sqlamLevel == MGRLVL_6)
                writeSQLUDTGRP(rsmeta, pmeta, jdbcElemNum, rtnOutput);
        } else {
            writeSQLDOPTGRP(rsmeta, pmeta, jdbcElemNum, rtnOutput);
        }

    }

    /**
     * Write variable character mixed byte or single byte
     * The preference is to write mixed byte if it is defined for the server,
     * since that is our default and we don't allow it to be changed, we always
     * write mixed byte.
     * 
     * @param s   string to write
     * @exception DRDAProtocolException
     */
    private void writeVCMorVCS(String s) throws DRDAProtocolException {
        //Write only VCM and 0 length for VCS

        if (s == null) {
            writer.writeShort(0);
            writer.writeShort(0);
            return;
        }

        // VCM
        writer.writeLDString(s);
        // VCS
        writer.writeShort(0);
    }

    /**
     * Write SQLUDTGRP (SQL Descriptor User-Defined Type Group Descriptor)
     * 
     * This is the format from the DRDA spec, Volume 1, section 5.6.4.10.
      * However, this format is not rich enough to carry the information needed
      * by JDBC. This format does not have a subtype code for JAVA_OBJECT and
      * this format does not convey the Java class name needed
      * by ResultSetMetaData.getColumnClassName().
     *
     *   SQLUDXTYPE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
      *                        Constants which map to java.sql.Types constants DISTINCT, STRUCT, and REF.
      *                        But DRDA does not define a constant which maps to java.sql.Types.JAVA_OBJECT.
     *   SQLUDTRDB; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
      *                       Database name.
     *   SQLUDTSCHEMA_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
     *   SQLUDTSCHEMA_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
      *                         Schema name. One of the above.
     *   SQLUDTNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
     *   SQLUDTNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
      *                         Unqualified UDT name. One of the above.
     *
     * Instead, we use the following format and only for communication between
      * Derby servers and Derby clients which are both at version 10.6 or higher.
      * For all other client/server combinations, we send null.
     *
     *   SQLUDTNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
     *   SQLUDTNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
      *                         Fully qualified UDT name. One of the above.
     *   SQLUDTCLASSNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override FdocaConstants.LONGVARCHAR_MAX_LEN
     *   SQLUDTCLASSNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override FdocaConstants.LONGVARCHAR_MAX_LEN
      *                         Name of the Java class bound to the UDT. One of the above.
     *
     * @param rsmeta   resultset meta data
     * @param pmeta      parameter meta data
     * @param jdbcElemNum   column number we are returning (in case of result set), or,
     *               parameter number (in case of parameter)
     * @param rtnOutput   whether this is for a result set   
     *
     * @throws DRDAProtocolException
      * @throws SQLException
     */
    private void writeSQLUDTGRP(ResultSetMetaData rsmeta, ParameterMetaData pmeta, int jdbcElemNum,
            boolean rtnOutput) throws DRDAProtocolException, SQLException {
        int jdbcType = rtnOutput ? rsmeta.getColumnType(jdbcElemNum) : pmeta.getParameterType(jdbcElemNum);

        if (!(jdbcType == Types.JAVA_OBJECT) || !appRequester.supportsUDTs()) {
            writer.writeByte(CodePoint.NULLDATA);
            return;
        }

        String typeName = rtnOutput ? rsmeta.getColumnTypeName(jdbcElemNum)
                : pmeta.getParameterTypeName(jdbcElemNum);
        String className = rtnOutput ? rsmeta.getColumnClassName(jdbcElemNum)
                : pmeta.getParameterClassName(jdbcElemNum);

        writeVCMorVCS(typeName);
        writeVCMorVCS(className);
    }

    private void writeSQLDOPTGRP(ResultSetMetaData rsmeta, ParameterMetaData pmeta, int jdbcElemNum,
            boolean rtnOutput) throws DRDAProtocolException, SQLException {

        writer.writeByte(0);
        //SQLUNAMED
        writer.writeShort(0);
        //SQLName
        writeVCMorVCS(rtnOutput ? rsmeta.getColumnName(jdbcElemNum) : null);
        //SQLLabel
        writeVCMorVCS(null);
        //SQLComments
        writeVCMorVCS(null);
        //SQLDUDTGRP 
        writeSQLUDTGRP(rsmeta, pmeta, jdbcElemNum, rtnOutput);
        //SQLDXGRP
        writeSQLDXGRP(rsmeta, pmeta, jdbcElemNum, rtnOutput);
    }

    private void writeSQLDXGRP(ResultSetMetaData rsmeta, ParameterMetaData pmeta, int jdbcElemNum,
            boolean rtnOutput) throws DRDAProtocolException, SQLException {
        // Null indicator indicates we have data
        writer.writeByte(0);
        //   SQLXKEYMEM; DRDA TYPE I2; ENVLID 0x04; Length Override 2
        // Hard to get primary key info. Send 0 for now
        writer.writeShort(0);
        //   SQLXUPDATEABLE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
        writer.writeShort(rtnOutput ? rsmeta.isWritable(jdbcElemNum) : false);

        //   SQLXGENERATED; DRDA TYPE I2; ENVLID 0x04; Length Override 2
        if (rtnOutput && rsmeta.isAutoIncrement(jdbcElemNum))
            writer.writeShort(2);
        else
            writer.writeShort(0);

        //   SQLXPARMMODE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
        if (pmeta != null && !rtnOutput) {
            int mode = pmeta.getParameterMode(jdbcElemNum);
            if (mode == JDBC30Translation.PARAMETER_MODE_UNKNOWN) {
                // For old style callable statements. We assume in/out if it
                // is an output parameter.
                int type = DRDAStatement
                        .getOutputParameterTypeFromClassName(pmeta.getParameterClassName(jdbcElemNum));
                if (type != DRDAStatement.NOT_OUTPUT_PARAM)
                    mode = JDBC30Translation.PARAMETER_MODE_IN_OUT;
            }
            writer.writeShort(mode);
        } else {
            writer.writeShort(0);
        }

        //   SQLXRDBNAM; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
        // JCC uses this as the catalog name so we will send null.
        writer.writeShort(0);

        //   SQLXCORNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
        //   SQLXCORNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
        writeVCMorVCS(null);

        //   SQLXBASENAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
        //   SQLXBASENAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
        writeVCMorVCS(rtnOutput ? rsmeta.getTableName(jdbcElemNum) : null);

        //   SQLXSCHEMA_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
        //   SQLXSCHEMA_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
        writeVCMorVCS(rtnOutput ? rsmeta.getSchemaName(jdbcElemNum) : null);

        //   SQLXNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
        //   SQLXNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
        writeVCMorVCS(rtnOutput ? rsmeta.getColumnName(jdbcElemNum) : null);

    }

    /**
     * Write Fdoca Value to client 
     * @param index     Index of column being returned
     * @param val       Value to write to client
     * @param drdaType  FD:OCA DRDA Type from FdocaConstants
     * @param precision Precision
     * @param stmt       Statement being processed
     * @param isParam   True when writing a value for a procedure parameter
     *
     * @exception DRDAProtocolException  
     * 
     * @exception SQLException
     *
     * @see FdocaConstants
     */

    protected void writeFdocaVal(int index, Object val, int drdaType, int precision, int scale, boolean valNull,
            DRDAStatement stmt, boolean isParam) throws DRDAProtocolException, SQLException {
        writeNullability(drdaType, valNull);

        if (!valNull) {
            int ndrdaType = drdaType | 1;
            long valLength = 0;
            switch (ndrdaType) {
            case DRDAConstants.DRDA_TYPE_NBOOLEAN:
                writer.writeBoolean(((Boolean) val).booleanValue());
                break;
            case DRDAConstants.DRDA_TYPE_NSMALL:
                // DB2 does not have a BOOLEAN java.sql.bit type,
                // so we need to send it as a small
                if (val instanceof Boolean) {
                    writer.writeShort(((Boolean) val).booleanValue());
                } else if (val instanceof Short)
                    writer.writeShort(((Short) val).shortValue());
                else if (val instanceof Byte)
                    writer.writeShort(((Byte) val).byteValue());
                else
                    writer.writeShort(((Integer) val).shortValue());
                break;
            case DRDAConstants.DRDA_TYPE_NINTEGER:
                writer.writeInt(((Integer) val).intValue());
                break;
            case DRDAConstants.DRDA_TYPE_NINTEGER8:
                writer.writeLong(((Long) val).longValue());
                break;
            case DRDAConstants.DRDA_TYPE_NFLOAT4:
                writer.writeFloat(((Float) val).floatValue());
                break;
            case DRDAConstants.DRDA_TYPE_NFLOAT8:
                writer.writeDouble(((Double) val).doubleValue());
                break;
            case DRDAConstants.DRDA_TYPE_NDECIMAL:
                if (precision == 0)
                    precision = FdocaConstants.NUMERIC_DEFAULT_PRECISION;
                BigDecimal bd = (java.math.BigDecimal) val;
                writer.writeBigDecimal(bd, precision, scale);
                break;
            case DRDAConstants.DRDA_TYPE_NDATE:
                writer.writeString(formatDate((java.sql.Date) val));
                break;
            case DRDAConstants.DRDA_TYPE_NTIME:
                writer.writeString(formatTime((Time) val));
                break;
            case DRDAConstants.DRDA_TYPE_NTIMESTAMP:
                writer.writeString(formatTimestamp((Timestamp) val));
                break;
            case DRDAConstants.DRDA_TYPE_NCHAR:
                writer.writeString(((String) val).toString());
                break;
            case DRDAConstants.DRDA_TYPE_NVARCHAR:
            case DRDAConstants.DRDA_TYPE_NVARMIX:
            case DRDAConstants.DRDA_TYPE_NLONG:
            case DRDAConstants.DRDA_TYPE_NLONGMIX:
                //WriteLDString and generate warning if truncated
                // which will be picked up by checkWarning()
                writer.writeLDString(val.toString(), index, stmt, isParam);
                break;
            case DRDAConstants.DRDA_TYPE_NLOBBYTES:
            case DRDAConstants.DRDA_TYPE_NLOBCMIXED:

                // do not send EXTDTA for lob of length 0, beetle 5967
                if (!((EXTDTAInputStream) val).isEmptyStream()) {
                    stmt.addExtDtaObject(val, index);

                    //indicate externalized and size is unknown.
                    writer.writeExtendedLength(0x8000);

                } else {
                    writer.writeExtendedLength(0);

                }

                break;

            case DRDAConstants.DRDA_TYPE_NFIXBYTE:
                writer.writeBytes((byte[]) val);
                break;
            case DRDAConstants.DRDA_TYPE_NVARBYTE:
            case DRDAConstants.DRDA_TYPE_NLONGVARBYTE:
                writer.writeLDBytes((byte[]) val, index);
                break;
            case DRDAConstants.DRDA_TYPE_NLOBLOC:
            case DRDAConstants.DRDA_TYPE_NCLOBLOC:
                writer.writeInt(((EngineLOB) val).getLocator());
                break;
            case DRDAConstants.DRDA_TYPE_NUDT:
            case DRDAConstants.DRDA_TYPE_NARRAY:
                writer.writeUDT(val, index);
                break;
            case DRDAConstants.DRDA_TYPE_NROWID:
                writer.writeRowId(val, index);
                break;
            default:
                if (SanityManager.DEBUG)
                    trace("ndrdaType is: " + ndrdaType);
                writer.writeLDString(val.toString(), index, stmt, isParam);
            }
        }
    }

    /**
     * write nullability if this is a nullable drdatype and FDOCA null
     * value if appropriate
     * @param drdaType      FDOCA type
     * @param valNull       true if this is a null value. False otherwise
     * 
     **/
    private void writeNullability(int drdaType, boolean valNull) {
        if (FdocaConstants.isNullable(drdaType)) {
            if (valNull)
                writer.writeByte(FdocaConstants.NULL_DATA);
            else {
                writer.writeByte(FdocaConstants.INDICATOR_NULLABLE);
            }
        }

    }

    /**
     * Convert a {@code java.sql.Date} to a string with the format expected
     * by the client.
     *
     * @param date the date to format
     * @return a string on the format YYYY-MM-DD representing the date
     * @see com.splicemachine.db.client.am.DateTime#dateBytesToDate
     */
    private String formatDate(java.sql.Date date) {
        Calendar cal = getGMTCalendar();
        cal.clear();
        cal.setTime(date);

        char[] buf = "YYYY-MM-DD".toCharArray();
        padInt(buf, 0, 4, cal.get(Calendar.YEAR));
        padInt(buf, 5, 2, cal.get(Calendar.MONTH) + 1);
        padInt(buf, 8, 2, cal.get(Calendar.DAY_OF_MONTH));

        return new String(buf);
    }

    /**
     * Convert a {@code java.sql.Time} to a string with the format expected
     * by the client.
     *
     * @param time the time to format
     * @return a string on the format HH:MM:SS representing the time
     * @see com.splicemachine.db.client.am.DateTime#timeBytesToTime
     */
    private String formatTime(Time time) {
        Calendar cal = getGMTCalendar();
        cal.clear();
        cal.setTime(time);

        char[] buf = "HH:MM:SS".toCharArray();
        padInt(buf, 0, 2, cal.get(Calendar.HOUR_OF_DAY));
        padInt(buf, 3, 2, cal.get(Calendar.MINUTE));
        padInt(buf, 6, 2, cal.get(Calendar.SECOND));

        return new String(buf);
    }

    /**
     * Convert a {@code java.sql.Timestamp} to a string with the format
     * expected by the client.
     *
     * @param ts the timestamp to format
     * @return a string on the format YYYY-MM-DD-HH.MM.SS.ffffff[fff]
     * @see com.splicemachine.db.client.am.DateTime#timestampBytesToTimestamp
     */
    private String formatTimestamp(Timestamp ts) {
        Calendar cal = getGMTCalendar();
        cal.clear();
        cal.setTime(ts);

        char[] buf = new char[appRequester.getTimestampLength()];
        padInt(buf, 0, 4, cal.get(Calendar.YEAR));
        buf[4] = '-';
        padInt(buf, 5, 2, cal.get(Calendar.MONTH) + 1);
        buf[7] = '-';
        padInt(buf, 8, 2, cal.get(Calendar.DAY_OF_MONTH));
        buf[10] = '-';
        padInt(buf, 11, 2, cal.get(Calendar.HOUR_OF_DAY));
        buf[13] = '.';
        padInt(buf, 14, 2, cal.get(Calendar.MINUTE));
        buf[16] = '.';
        padInt(buf, 17, 2, cal.get(Calendar.SECOND));
        buf[19] = '.';

        int nanos = ts.getNanos();
        if (appRequester.supportsTimestampNanoseconds()) {
            padInt(buf, 20, 9, nanos);
        } else {
            padInt(buf, 20, 6, nanos / 1000);
        }

        return new String(buf);
    }

    /**
     * Insert an integer into a char array and pad it with leading zeros if
     * its string representation is shorter than {@code length} characters.
     *
     * @param buf the char array
     * @param offset where in the array to start inserting the value
     * @param length the desired length of the inserted string
     * @param value the integer value to insert
     */
    private void padInt(char[] buf, int offset, int length, int value) {
        final int radix = 10;
        for (int i = offset + length - 1; i >= offset; i--) {
            buf[i] = Character.forDigit(value % radix, radix);
            value /= radix;
        }
    }

    /**
     * Methods to keep track of required codepoints
     */
    /**
     * Copy a list of required code points to template for checking
     *
     * @param req list of required codepoints
     */
    private void copyToRequired(int[] req) {
        currentRequiredLength = req.length;
        if (currentRequiredLength > required.length)
            required = new int[currentRequiredLength];
        System.arraycopy(req, 0, required, 0, req.length);
    }

    /**
     * Remove codepoint from required list
      *
     * @param codePoint - code point to be removed
     */
    private void removeFromRequired(int codePoint) {
        for (int i = 0; i < currentRequiredLength; i++)
            if (required[i] == codePoint)
                required[i] = 0;

    }

    /**
     * Check whether we have seen all the required code points
     *
     * @param codePoint code point for which list of code points is required
     */
    private void checkRequired(int codePoint) throws DRDAProtocolException {
        int firstMissing = 0;
        for (int i = 0; i < currentRequiredLength; i++) {
            if (required[i] != 0) {
                firstMissing = required[i];
                break;
            }
        }
        if (firstMissing != 0)
            missingCodePoint(firstMissing);
    }

    /**
     * Error routines
     */
    /**
     * Seen too many of this code point
     *
     * @param codePoint  code point which has been duplicated
     *
     * @exception DRDAProtocolException
     */
    private void tooMany(int codePoint) throws DRDAProtocolException {
        throwSyntaxrm(CodePoint.SYNERRCD_TOO_MANY, codePoint);
    }

    /**
     * Object too big
     *
     * @param codePoint  code point with too big object
     * @exception DRDAProtocolException
     */
    private void tooBig(int codePoint) throws DRDAProtocolException {
        throwSyntaxrm(CodePoint.SYNERRCD_TOO_BIG, codePoint);
    }

    /**
     * Invalid non-db client tried to connect.
     * thrown a required Value not found error and log a message to db.log
     * 
     * @param prdid product id that does not match DNC 
     * @throws DRDAProtocolException
     */
    private void invalidClient(String prdid) throws DRDAProtocolException {
        Monitor.logMessage(
                new Date() + " : " + server.localizeMessage("DRDA_InvalidClient.S", new String[] { prdid }));
        requiredValueNotFound(CodePoint.PRDID);

    }

    /*** Required value not found.
     * 
     * @param codePoint code point with invalid value
     * 
     */
    private void requiredValueNotFound(int codePoint) throws DRDAProtocolException {
        throwSyntaxrm(CodePoint.SYNERRCD_REQ_VAL_NOT_FOUND, codePoint);
    }

    /**
     * Object length not allowed
     *
     * @param codePoint  code point with bad object length
     * @exception DRDAProtocolException
     */
    private void badObjectLength(int codePoint) throws DRDAProtocolException {
        throwSyntaxrm(CodePoint.SYNERRCD_OBJ_LEN_NOT_ALLOWED, codePoint);
    }

    /**
     * RDB not found
     *
     * @param rdbnam  name of database
     * @exception DRDAProtocolException
     */
    private void rdbNotFound(String rdbnam) throws DRDAProtocolException {
        Object[] oa = { rdbnam };
        throw new DRDAProtocolException(DRDAProtocolException.DRDA_Proto_RDBNFNRM, this, 0,
                DRDAProtocolException.NO_ASSOC_ERRCD, oa);
    }

    /**
     * Invalid value for this code point
     *
     * @param codePoint  code point value
     * @exception DRDAProtocolException
     */
    private void invalidValue(int codePoint) throws DRDAProtocolException {
        throwSyntaxrm(CodePoint.SYNERRCD_REQ_VAL_NOT_FOUND, codePoint);
    }

    /**
     * Invalid codepoint for this command
     *
     * @param codePoint code point value
     *
     * @exception DRDAProtocolException
     */
    protected void invalidCodePoint(int codePoint) throws DRDAProtocolException {
        throwSyntaxrm(CodePoint.SYNERRCD_INVALID_CP_FOR_CMD, codePoint);
    }

    /**
     * Don't support this code point
     *
     * @param codePoint  code point value
     * @exception DRDAProtocolException
     */
    protected void codePointNotSupported(int codePoint) throws DRDAProtocolException {
        throw new DRDAProtocolException(DRDAProtocolException.DRDA_Proto_CMDNSPRM, this, codePoint,
                DRDAProtocolException.NO_ASSOC_ERRCD);
    }

    /**
     * Don't support this value
     *
     * @param codePoint  code point value
     * @exception DRDAProtocolException
     */
    private void valueNotSupported(int codePoint) throws DRDAProtocolException {
        throw new DRDAProtocolException(DRDAProtocolException.DRDA_Proto_VALNSPRM, this, codePoint,
                DRDAProtocolException.NO_ASSOC_ERRCD);
    }

    /**
     * Verify that the code point is the required code point
     *
     * @param codePoint code point we have
     * @param reqCodePoint code point required at this time
      *
     * @exception DRDAProtocolException
     */
    private void verifyRequiredObject(int codePoint, int reqCodePoint) throws DRDAProtocolException {
        if (codePoint != reqCodePoint) {
            throwSyntaxrm(CodePoint.SYNERRCD_REQ_OBJ_NOT_FOUND, codePoint);
        }
    }

    /**
     * Verify that the code point is in the right order
     *
     * @param codePoint code point we have
     * @param reqCodePoint code point required at this time
      *
     * @exception DRDAProtocolException
     */
    private void verifyInOrderACCSEC_SECCHK(int codePoint, int reqCodePoint) throws DRDAProtocolException {
        if (codePoint != reqCodePoint) {
            throw new DRDAProtocolException(DRDAProtocolException.DRDA_Proto_PRCCNVRM, this, codePoint,
                    CodePoint.PRCCNVCD_ACCSEC_SECCHK_WRONG_STATE);
        }
    }

    /**
     * Database name given under code point doesn't match previous database names
     *
     * @param codePoint codepoint where the mismatch occurred
      *
     * @exception DRDAProtocolException
     */
    private void rdbnamMismatch(int codePoint) throws DRDAProtocolException {
        throw new DRDAProtocolException(DRDAProtocolException.DRDA_Proto_PRCCNVRM, this, codePoint,
                CodePoint.PRCCNVCD_RDBNAM_MISMATCH);
    }

    /**
     * Close the current session
     */
    private void closeSession() {
        if (session == null)
            return;

        /* DERBY-2220: Rollback the current XA transaction if it is
           still associated with the connection. */
        if (xaProto != null)
            xaProto.rollbackCurrentTransaction();

        server.removeFromSessionTable(session.connNum);
        try {
            session.close();
        } catch (SQLException se) {
            // If something went wrong closing down the session.
            // Print an error to the console and close this 
            //thread. (6013)
            sendUnexpectedException(se);
            close();
        } finally {
            session = null;
            database = null;
            appRequester = null;
            sockis = null;
            sockos = null;
            databaseAccessException = null;
        }
    }

    /**
     * Handle Exceptions - write error protocol if appropriate and close session
     *   or thread as appropriate
     */
    private void handleException(Exception e) {
        try {
            if (e instanceof DRDAProtocolException) {
                // protocol error - write error message
                sendProtocolException((DRDAProtocolException) e);
            } else {
                // something unexpected happened
                sendUnexpectedException(e);
                server.consoleExceptionPrintTrace(e);
            }
        } finally {
            // always close the session and stop the thread after handling
            // these exceptions
            closeSession();
            close();
        }
    }

    /**
     * Notice the client about a protocol error.
     *
     * @param de <code>DRDAProtocolException</code> to be sent
     */
    private void sendProtocolException(DRDAProtocolException de) {
        String dbname = null;
        if (database != null) {
            dbname = database.getDatabaseName();
        }

        try {
            println2Log(dbname, session.drdaID, de.getMessage());
            server.consoleExceptionPrintTrace(de);
            reader.clearBuffer();
            de.write(writer);
            finalizeChain();
        } catch (DRDAProtocolException ioe) {
            // There may be an IO exception in the write.
            println2Log(dbname, session.drdaID, de.getMessage());
            server.consoleExceptionPrintTrace(ioe);
        }
    }

    /**
     * Send unpexpected error to the client
     * @param e Exception to be sent
     */
    private void sendUnexpectedException(Exception e) {

        DRDAProtocolException unExpDe;
        String dbname = null;
        try {
            if (database != null)
                dbname = database.getDatabaseName();
            println2Log(dbname, session.drdaID, e.getMessage());
            server.consoleExceptionPrintTrace(e);
            unExpDe = DRDAProtocolException.newAgentError(this, CodePoint.SVRCOD_PRMDMG, dbname, e.getMessage());

            reader.clearBuffer();
            unExpDe.write(writer);
            finalizeChain();
        } catch (DRDAProtocolException nde) {
            // we can't tell the client, but we tried.
        }

    }

    /**
     * Test if DRDA connection thread is closed
     *
     * @return true if close; false otherwise
     */
    private boolean closed() {
        synchronized (closeSync) {
            return close;
        }
    }

    /**
     * Get whether connections are logged
     *
     * @return true if connections are being logged; false otherwise
     */
    private boolean getLogConnections() {
        synchronized (logConnectionsSync) {
            return logConnections;
        }
    }

    /**
     * Get time slice value for length of time to work on a session
     *
     * @return time slice
     */
    private long getTimeSlice() {
        synchronized (timeSliceSync) {
            return timeSlice;
        }
    }

    /**
     * Send string to console
     *
     * @param value - value to print on console
     */
    protected void trace(String value) {
        if (SanityManager.DEBUG && server.debugOutput == true)
            server.consoleMessage(value, true);
    }

    /**
     * Sends a trace string to the console when reading an EXTDTA value (if
     * tracing is enabled).
     *
     * @param drdaType the DRDA type of the EXTDTA value
     * @param index the one-based parameter index
     * @param stream the stream being read
     * @param streamLOB whether or not the value is being streamed as the last
     *      parameter value in the DRDA protocol flow
     * @param encoding the encoding of the data, if any
     */
    private void traceEXTDTARead(int drdaType, int index, EXTDTAReaderInputStream stream, boolean streamLOB,
            String encoding) {
        if (SanityManager.DEBUG && server.debugOutput == true) {
            StringBuffer sb = new StringBuffer("Reading/setting EXTDTA: ");
            // Data: t<type>/i<ob_index>/<streamLOB>/<encoding>/
            //       <statusByteExpected>/b<byteLength>
            sb.append("t").append(drdaType).append("/i").append(index).append("/").append(streamLOB).append("/")
                    .append(encoding).append("/").append(stream.readStatusByte).append("/b");
            if (stream == null) {
                sb.append("NULL");
            } else if (stream.isLayerBStream()) {
                sb.append("UNKNOWN_LENGTH");
            } else {
                sb.append(((StandardEXTDTAReaderInputStream) stream).getLength());
            }
            trace(sb.toString());
        }
    }

    /***
     * Show runtime memory
     *
     ***/
    public static void showmem() {
        Runtime rt = null;
        Date d = null;
        rt = Runtime.getRuntime();
        rt.gc();
        d = new Date();
        System.out.println("total memory: " + rt.totalMemory() + " free: " + rt.freeMemory() + " " + d.toString());

    }

    /**
     * convert byte array to a Hex string
     * 
     * @param buf buffer to  convert
     * @return hex string representation of byte array
     */
    private String convertToHexString(byte[] buf) {
        StringBuffer str = new StringBuffer();
        str.append("0x");
        String val;
        int byteVal;
        for (int i = 0; i < buf.length; i++) {
            byteVal = buf[i] & 0xff;
            val = Integer.toHexString(byteVal);
            if (val.length() < 2)
                str.append("0");
            str.append(val);
        }
        return str.toString();
    }

    /**
     * check that the given typdefnam is acceptable
     * 
     * @param typdefnam 
      *
      * @exception DRDAProtocolException
     */
    private void checkValidTypDefNam(String typdefnam) throws DRDAProtocolException {
        if (typdefnam.equals("QTDSQL370"))
            return;
        if (typdefnam.equals("QTDSQL400"))
            return;
        if (typdefnam.equals("QTDSQLX86"))
            return;
        if (typdefnam.equals("QTDSQLASC"))
            return;
        if (typdefnam.equals("QTDSQLVAX"))
            return;
        if (typdefnam.equals("QTDSQLJVM"))
            return;
        invalidValue(CodePoint.TYPDEFNAM);
    }

    /**
     * Check that the length is equal to the required length for this codepoint
     *
     * @param codepoint   codepoint we are checking
     * @param reqlen   required length
     * 
      * @exception DRDAProtocolException
     */
    private void checkLength(int codepoint, int reqlen) throws DRDAProtocolException {
        long len = reader.getDdmLength();
        if (len < reqlen)
            badObjectLength(codepoint);
        else if (len > reqlen)
            tooBig(codepoint);
    }

    /**
     * Read and check a boolean value
     * 
     * @param codepoint codePoint to be used in error reporting
     * @return true or false depending on boolean value read
     *
     * @exception DRDAProtocolException
     */
    private boolean readBoolean(int codepoint) throws DRDAProtocolException {
        checkLength(codepoint, 1);
        byte val = reader.readByte();
        if (val == CodePoint.TRUE)
            return true;
        else if (val == CodePoint.FALSE)
            return false;
        else
            invalidValue(codepoint);
        return false; //to shut the compiler up
    }

    /**
     * Create a new database and intialize the 
      * DRDAConnThread database.
      * 
      * @param dbname database name to initialize. If 
      * dbnam is non null, add database to the current session
     *
     */
    private void initializeDatabase(String dbname) {
        Database db;
        if (appRequester.isXARequester()) {
            db = new XADatabase(dbname);
        } else
            db = new Database(dbname);
        if (dbname != null) {
            session.addDatabase(db);
            session.database = db;
        }
        database = db;
    }

    /**
     * Set the current database
     * 
     * @param codePoint    codepoint we are processing
     *
     * @exception DRDAProtocolException
     */
    private void setDatabase(int codePoint) throws DRDAProtocolException {
        String rdbnam = parseRDBNAM();
        // using same database so we are done
        if (database != null && database.getDatabaseName().equals(rdbnam))
            return;
        Database d = session.getDatabase(rdbnam);
        if (d == null)
            rdbnamMismatch(codePoint);
        else
            database = d;
        session.database = d;
    }

    /**
     * Write ENDUOWRM
     * Instance Variables
     *  SVCOD - severity code - WARNING - required
     *  UOWDSP - Unit of Work Disposition - required
     *  RDBNAM - Relational Database name - optional
     *  SRVDGN - Server Diagnostics information - optional
     *
     * @param opType - operation type 1 - commit, 2 -rollback
     */
    private void writeENDUOWRM(int opType) {
        writer.createDssReply();
        writer.startDdm(CodePoint.ENDUOWRM);
        writer.writeScalar2Bytes(CodePoint.SVRCOD, CodePoint.SVRCOD_WARNING);
        writer.writeScalar1Byte(CodePoint.UOWDSP, opType);
        writer.endDdmAndDss();
    }

    void writeEXTDTA(DRDAStatement stmt) throws SQLException, DRDAProtocolException {
        ArrayList extdtaValues = stmt.getExtDtaObjects();
        // build the EXTDTA data, if necessary
        if (extdtaValues == null)
            return;
        boolean chainFlag, chainedWithSameCorrelator;
        boolean writeNullByte = false;

        for (int i = 0; i < extdtaValues.size(); i++) {
            // is this the last EXTDTA to be built?
            if (i != extdtaValues.size() - 1) { // no
                chainFlag = true;
                chainedWithSameCorrelator = true;
            } else { // yes
                chainFlag = false; //last blob DSS stream itself is NOT chained with the NEXT DSS
                chainedWithSameCorrelator = false;
            }

            if (sqlamLevel >= MGRLVL_7)
                if (stmt.isExtDtaValueNullable(i))
                    writeNullByte = true;

            Object o = extdtaValues.get(i);
            if (o instanceof EXTDTAInputStream) {
                EXTDTAInputStream stream = (EXTDTAInputStream) o;

                try {
                    stream.initInputStream();
                    writer.writeScalarStream(chainedWithSameCorrelator, CodePoint.EXTDTA, stream, writeNullByte);

                } finally {
                    // close the stream when done
                    closeStream(stream);
                }

            }
        }
        // reset extdtaValues after sending
        stmt.clearExtDtaObjects();

    }

    /**
     * Check SQLWarning and write SQLCARD as needed.
     * 
     * @param conn       connection to check
     * @param stmt       statement to check
     * @param rs       result set to check
     * @param updateCount    update count to include in SQLCARD
     * @param alwaysSend    whether always send SQLCARD regardless of
     *                  the existance of warnings
     * @param sendWarn    whether to send any warnings or not. 
     *
     * @exception DRDAProtocolException
     */
    private void checkWarning(Connection conn, Statement stmt, ResultSet rs, int updateCount, boolean alwaysSend,
            boolean sendWarn) throws DRDAProtocolException, SQLException {
        // instead of writing a chain of sql warning, we send the first one, this is
        // jcc/db2 limitation, see beetle 4629
        SQLWarning warning = null;
        SQLWarning reportWarning = null;
        try {
            if (stmt != null) {
                warning = stmt.getWarnings();
                if (warning != null) {
                    stmt.clearWarnings();
                    reportWarning = warning;
                }
            }
            if (rs != null) {
                warning = rs.getWarnings();
                if (warning != null) {
                    rs.clearWarnings();
                    if (reportWarning == null)
                        reportWarning = warning;
                }
            }
            if (conn != null) {
                warning = conn.getWarnings();
                if (warning != null) {
                    conn.clearWarnings();
                    if (reportWarning == null)
                        reportWarning = warning;
                }
            }

        } catch (SQLException se) {
            if (SanityManager.DEBUG)
                trace("got SQLException while trying to get warnings.");
        }

        if ((alwaysSend || reportWarning != null) && sendWarn)
            writeSQLCARDs(reportWarning, updateCount);
    }

    boolean hasSession() {
        return session != null;
    }

    long getBytesRead() {
        return reader.totalByteCount;
    }

    long getBytesWritten() {
        return writer.totalByteCount;
    }

    protected String buildRuntimeInfo(String indent, LocalizedResource localLangUtil) {
        String s = "";
        if (!hasSession())
            return s;
        else
            s += session.buildRuntimeInfo("", localLangUtil);
        s += "\n";
        return s;
    }

    /**
     * Finalize the current DSS chain and send it if
     * needed.
     */
    private void finalizeChain() throws DRDAProtocolException {

        writer.finalizeChain(reader.getCurrChainState(), getOutputStream());
        return;

    }

    /**
     *  Validate SECMEC_USRSSBPWD (Strong Password Substitute) can be used as
     *  DRDA security mechanism.
     *
     *   Here we check that the target server can support SECMEC_USRSSBPWD
     *   security mechanism based on the environment, application
     *   requester's identity (PRDID) and connection URL.
     *
     *   IMPORTANT NOTE:
     *   --------------
     *   SECMEC_USRSSBPWD is ONLY supported by the target server if:
     *       - current authentication provider is Derby BUILTIN or
     *         NONE. (database / system level) (Phase I)
     *      - database-level password must have been encrypted with the
     *        SHA-1 based authentication scheme
     *      - Application requester is 'DNC' (Derby Network Client)
     *        (Phase I)
     *
     *  @return security check code - 0 if everything O.K.
     */
    private int validateSecMecUSRSSBPWD() throws DRDAProtocolException {
        String dbName = null;
        AuthenticationService authenticationService = null;
        com.splicemachine.db.iapi.db.Database databaseObj = null;
        String srvrlslv = appRequester.srvrlslv;

        // Check if application requester is the Derby Network Client (DNC)
        //
        // We use a trick here - as the product ID is not yet available
        // since ACCRDB message is only coming later, we check the server
        // release level field sent as part of the initial EXCSAT message;
        // indeed, the product ID (PRDID) is prefixed to in the field.
        // Derby always sets it as part of the EXCSAT message so if it is
        // not available, we stop here and inform the requester that
        // SECMEC_USRSSBPWD cannot be supported for this connection.
        if ((srvrlslv == null) || (srvrlslv.length() == 0) || (srvrlslv.length() < CodePoint.PRDID_MAX)
                || (srvrlslv.indexOf(DRDAConstants.DERBY_DRDA_CLIENT_ID) == -1))
            return CodePoint.SECCHKCD_NOTSUPPORTED; // Not Supported

        // Client product version is extracted from the srvrlslv field.
        // srvrlslv has the format <PRDID>/<ALTERNATE VERSION FORMAT>
        // typically, a known Derby client has a four part version number
        // with a pattern such as DNC10020/10.2.0.3 alpha. If the alternate
        // version format is not specified, clientProductVersion_ will just
        // be set to the srvrlslvl. Final fallback will be the product id.
        //
        // SECMEC_USRSSBPWD is only supported by the Derby engine and network
        // server code starting at version major '10' and minor '02'. Hence,
        // as this is the same for the db client driver, we need to ensure
        // our DNC client is at version and release level of 10.2 at least.
        // We set the client version in the application requester and check
        // if it is at the level we require at a minimum.
        appRequester.setClientVersion(srvrlslv.substring(0, (int) CodePoint.PRDID_MAX));

        if (appRequester.supportsSecMecUSRSSBPWD() == false)
            return CodePoint.SECCHKCD_NOTSUPPORTED; // Not Supported

        dbName = database.getShortDbName();
        // Check if the database is available (booted)
        // 
        // First we need to have the database name available and it should
        // have been set as part of the ACCSEC request (in the case of a Derby
        // 'DNC' client)
        if ((dbName == null) || (dbName.length() == 0)) {
            // No database specified in the connection URL attributes
            //
            // In this case, we get the authentication service handle from the
            // local driver, as the requester may simply be trying to shutdown
            // the engine.
            authenticationService = ((InternalDriver) NetworkServerControlImpl.getDriver())
                    .getAuthenticationService();
        } else {
            // We get the authentication service from the database as this
            // last one might have specified its own auth provider (at the
            // database level).
            // 
            // if monitor is never setup by any ModuleControl, getMonitor
            // returns null and no Derby database has been booted. 
            if (Monitor.getMonitor() != null)
                databaseObj = (com.splicemachine.db.iapi.db.Database) Monitor.findService(Property.DATABASE_MODULE,
                        dbName);

            if (databaseObj == null) {
                // If database is not found, try connecting to it. 
                database.makeDummyConnection();

                // now try to find it again
                databaseObj = (com.splicemachine.db.iapi.db.Database) Monitor.findService(Property.DATABASE_MODULE,
                        dbName);
            }

            // If database still could not be found, it means the database
            // does not exist - we just return security mechanism not
            // supported down below as we could not verify we can handle
            // it.
            try {
                if (databaseObj != null)
                    authenticationService = databaseObj.getAuthenticationService();
            } catch (StandardException se) {
                println2Log(null, session.drdaID, se.getMessage());
                // Local security service non-retryable error.
                return CodePoint.SECCHKCD_0A;
            }

        }

        // Now we check if the authentication provider is NONE or BUILTIN
        if (authenticationService != null) {
            String authClassName = authenticationService.getClass().getName();

            if (!authClassName.equals(AUTHENTICATION_PROVIDER_BUILTIN_CLASS)
                    && !authClassName.equals(AUTHENTICATION_PROVIDER_NONE_CLASS))
                return CodePoint.SECCHKCD_NOTSUPPORTED; // Not Supported
        }

        // SECMEC_USRSSBPWD target initialization
        try {
            myTargetSeed = DecryptionManager.generateSeed();
            database.secTokenOut = myTargetSeed;
        } catch (SQLException se) {
            println2Log(null, session.drdaID, se.getMessage());
            // Local security service non-retryable error.
            return CodePoint.SECCHKCD_0A;
        }

        return 0; // SECMEC_USRSSBPWD is supported
    }

    /**
     * Close a stream.
     *
     * @param stream the stream to close (possibly {@code null})
     * @throws SQLException wrapped around an {@code IOException} if closing
     * the stream failed
     */
    private static void closeStream(InputStream stream) throws SQLException {
        try {
            if (stream != null) {
                stream.close();
            }
        } catch (IOException e) {
            throw Util.javaException(e);
        }
    }

    private static InputStream convertAsByteArrayInputStream(EXTDTAReaderInputStream stream) throws IOException {

        // Suppress the exception that may be thrown when reading the status
        // byte here, we want the embedded statement to fail while executing.
        stream.setSuppressException(true);

        final int byteArrayLength = stream instanceof StandardEXTDTAReaderInputStream
                ? (int) ((StandardEXTDTAReaderInputStream) stream).getLength()
                : 1 + stream.available(); // +1 to avoid infinite loop

        // TODO: We will run into OOMEs for large values here.
        //       Could avoid this by saving value temporarily to disk, for
        //       instance by using the existing LOB code.
        PublicBufferOutputStream pbos = new PublicBufferOutputStream(byteArrayLength);

        byte[] buffer = new byte[Math.min(byteArrayLength, 32 * 1024)];

        int c = 0;

        while ((c = stream.read(buffer, 0, buffer.length)) > -1) {
            pbos.write(buffer, 0, c);
        }

        // Check if the client driver encountered any errors when reading the
        // source on the client side.
        if (stream.isStatusSet() && stream.getStatus() != DRDAConstants.STREAM_OK) {
            // Create a stream that will just fail when accessed.
            return new FailingEXTDTAInputStream(stream.getStatus());
        } else {
            return new ByteArrayInputStream(pbos.getBuffer(), 0, pbos.getCount());
        }

    }

    private static class PublicBufferOutputStream extends ByteArrayOutputStream {

        PublicBufferOutputStream(int size) {
            super(size);
        }

        public byte[] getBuffer() {
            return buf;
        }

        public int getCount() {
            return count;
        }

    }

    /**
     * Sets the specified character EXTDTA parameter of the embedded statement.
     *
     * @param stmt the DRDA statement to use
     * @param i the one-based index of the parameter
     * @param extdtaStream the EXTDTA stream to read data from
     * @param streamLOB whether or not the stream content is streamed as the
     *      last value in the DRDA protocol flow
     * @param encoding the encoding of the EXTDTA stream
     * @throws IOException if reading from the stream fails
     * @throws SQLException if setting the stream fails
     */
    private static void setAsCharacterStream(DRDAStatement stmt, int i, EXTDTAReaderInputStream extdtaStream,
            boolean streamLOB, String encoding) throws IOException, SQLException {
        PreparedStatement ps = stmt.getPreparedStatement();
        EnginePreparedStatement engnps = (EnginePreparedStatement) ps;

        // DERBY-3085. Save the stream so it can be drained later
        // if not  used.
        if (streamLOB)
            stmt.setStreamedParameter(extdtaStream);

        final InputStream is = streamLOB ? (InputStream) extdtaStream : convertAsByteArrayInputStream(extdtaStream);

        final InputStreamReader streamReader = new InputStreamReader(is, encoding);

        engnps.setCharacterStream(i, streamReader);
    }

    /**
     * Sets the specified binary EXTDTA parameter of the embedded statement.
     *
     * @param stmt the DRDA statement to use
     * @param index the one-based index of the parameter
     * @param stream the EXTDTA stream to read data from
     * @param streamLOB whether or not the stream content is streamed as the
     *      last value in the DRDA protocol flow
     * @throws IOException if reading from the stream fails
     * @throws SQLException  if setting the stream fails
     */
    private static void setAsBinaryStream(DRDAStatement stmt, int index, EXTDTAReaderInputStream stream,
            boolean streamLOB) throws IOException, SQLException {
        int type = stmt.getParameterMetaData().getParameterType(index);
        boolean useSetBinaryStream = (type == Types.BLOB);
        PreparedStatement ps = stmt.getPreparedStatement();

        if (streamLOB && useSetBinaryStream) {
            // Save the streamed parameter so we can drain it if it does not
            // get used by embedded when the statement is executed. DERBY-3085
            stmt.setStreamedParameter(stream);
            if (stream == null) {
                ps.setBytes(index, null);
            } else if (!stream.isLayerBStream()) {
                int length = (int) ((StandardEXTDTAReaderInputStream) stream).getLength();
                ps.setBinaryStream(index, stream, length);

            } else {
                ((EnginePreparedStatement) ps).setBinaryStream(index, stream);
            }
        } else {
            if (stream == null) {
                ps.setBytes(index, null);
            } else {
                InputStream bais = convertAsByteArrayInputStream(stream);
                ps.setBinaryStream(index, bais, bais.available());
            }
        }
    }

}