All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.sqlserver.jdbc.SQLServerResultSet Maven / Gradle / Ivy

/*
 * Microsoft JDBC Driver for SQL Server
 * 
 * Copyright(c) Microsoft Corporation All rights reserved.
 * 
 * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
 */

package com.microsoft.sqlserver.jdbc;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.NClob;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.text.MessageFormat;
import java.util.Calendar;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

/**
 * Indicates the type of the row received from the server
 */
enum RowType {
    ROW,
    NBCROW,
    UNKNOWN,
}

/**
 * Top-level JDBC ResultSet implementation
 */
public class SQLServerResultSet implements ISQLServerResultSet {

    /** Generate the statement's logging ID */
    private static final AtomicInteger lastResultSetID = new AtomicInteger(0);
    private final String traceID;

    private static int nextResultSetID() {
        return lastResultSetID.incrementAndGet();
    }

    final static java.util.logging.Logger logger = java.util.logging.Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerResultSet");

    public String toString() {
        return traceID;
    }

    String logCursorState() {
        return " currentRow:" + currentRow + " numFetchedRows:" + numFetchedRows + " rowCount:" + rowCount;
    }

    protected static final java.util.logging.Logger loggerExternal = java.util.logging.Logger.getLogger("com.microsoft.sqlserver.jdbc.ResultSet");

    final private String loggingClassName;

    String getClassNameLogging() {
        return loggingClassName;
    }

    /** the statement that generated this result set */
    private final SQLServerStatement stmt;

    /** max rows to return from this result set */
    private final int maxRows;

    /** the meta data for this result set */
    private ResultSetMetaData metaData;

    /** is the result set close */
    private boolean isClosed = false;

    private final int serverCursorId;

    /** the intended fetch direction to optimize cursor performance */
    private int fetchDirection;

    /** the desired fetch size to optimize cursor performance */
    private int fetchSize;

    /** true if the cursor is positioned on the insert row */
    private boolean isOnInsertRow = false;

    /** true if the last value read was SQL NULL */
    private boolean lastValueWasNull = false;

    /** The index (1-based) of the last column in the current row that has been marked for reading */
    private int lastColumnIndex;

    // Indicates if the null bit map is loaded for the current row
    // in the resultset
    private boolean areNullCompressedColumnsInitialized = false;

    // Indicates the type of the current row in the result set
    private RowType resultSetCurrentRowType = RowType.UNKNOWN;

    // getter for resultSetCurrentRowType
    final RowType getCurrentRowType() {
        return resultSetCurrentRowType;
    }

    // setter for resultSetCurrentRowType
    final void setCurrentRowType(RowType rowType) {
        resultSetCurrentRowType = rowType;
    }

    /**
     * Currently active Stream Note only one stream can be active at a time, JDBC spec calls for the streams to be closed when a column or row move
     * occurs
     */
    private Closeable activeStream;

    /**
     * A window of fetchSize quickly accessible rows for scrollable result sets
     */
    private final ScrollWindow scrollWindow;

    /**
     * Current row, which is either the actual (1-based) value or one of the special values defined below.
     */
    private static final int BEFORE_FIRST_ROW = 0;
    private static final int AFTER_LAST_ROW = -1;
    private static final int UNKNOWN_ROW = -2;
    private int currentRow = BEFORE_FIRST_ROW;

    /** Flag set to true if the current row was updated through this ResultSet object */
    private boolean updatedCurrentRow = false;

    final boolean getUpdatedCurrentRow() {
        return updatedCurrentRow;
    }

    final void setUpdatedCurrentRow(boolean rowUpdated) {
        updatedCurrentRow = rowUpdated;
    }

    /** Flag set to true if the current row was deleted through this ResultSet object */
    private boolean deletedCurrentRow = false;

    final boolean getDeletedCurrentRow() {
        return deletedCurrentRow;
    }

    final void setDeletedCurrentRow(boolean rowDeleted) {
        deletedCurrentRow = rowDeleted;
    }

    /**
     * Count of rows in this result set.
     *
     * The number of rows in the result set may be known when this ResultSet object is created, after the first full traversal of the result set, or
     * possibly never (as is the case with DYNAMIC cursors).
     */
    static final int UNKNOWN_ROW_COUNT = -3;
    private int rowCount;

    /** The current row's column values */
    private final Column[] columns;

    // The CekTable retrieved from the COLMETADATA token for this resultset.
    private CekTable cekTable = null;

    /* Gets the CekTable */
    CekTable getCekTable() {
        return cekTable;
    }

    final void setColumnName(int index,
            String name) {
        columns[index - 1].setColumnName(name);
    }

    /**
     * Skips columns between the last marked column and the target column, inclusive, optionally discarding their values as they are skipped.
     */
    private void skipColumns(int columnsToSkip,
            boolean discardValues) throws SQLServerException {
        assert lastColumnIndex >= 1;
        assert 0 <= columnsToSkip && columnsToSkip <= columns.length;

        for (int columnsSkipped = 0; columnsSkipped < columnsToSkip; ++columnsSkipped) {
            Column column = getColumn(lastColumnIndex++);
            column.skipValue(tdsReader, discardValues && isForwardOnly());
            if (discardValues)
                column.clear();
        }
    }

    /** TDS reader from which row values are read */
    private TDSReader tdsReader;

    private final FetchBuffer fetchBuffer;

    /**
     * Make a new result set
     * 
     * @param stmtIn
     *            the generating statement
     */
    SQLServerResultSet(SQLServerStatement stmtIn) throws SQLServerException {
        int resultSetID = nextResultSetID();
        loggingClassName = "com.microsoft.sqlserver.jdbc.SQLServerResultSet" + ":" + resultSetID;
        traceID = "SQLServerResultSet:" + resultSetID;

        // Common initializer class for server-cursored and client-cursored ResultSets.
        // The common initializer builds columns from the column metadata and other table
        // info when present. Specialized subclasses take care of behavior that is specific
        // to either server-cursored or client-cursored ResultSets.
        abstract class CursorInitializer extends TDSTokenHandler {
            abstract int getRowCount();

            abstract int getServerCursorId();

            private StreamColumns columnMetaData = null;
            private StreamColInfo colInfo = null;
            private StreamTabName tabName = null;

            final Column[] buildColumns() throws SQLServerException {
                return columnMetaData.buildColumns(colInfo, tabName);
            }

            CursorInitializer(String name) {
                super(name);
            }

            boolean onColInfo(TDSReader tdsReader) throws SQLServerException {
                colInfo = new StreamColInfo();
                colInfo.setFromTDS(tdsReader);
                return true;
            }

            boolean onTabName(TDSReader tdsReader) throws SQLServerException {
                tabName = new StreamTabName();
                tabName.setFromTDS(tdsReader);
                return true;
            }

            boolean onColMetaData(TDSReader tdsReader) throws SQLServerException {
                columnMetaData = new StreamColumns(Util.shouldHonorAEForRead(stmt.stmtColumnEncriptionSetting, stmt.connection));
                columnMetaData.setFromTDS(tdsReader);
                cekTable = columnMetaData.getCekTable();
                return true;
            }
        }

        // Server-cursor initializer expects a cursorID and row count to be
        // returned in OUT parameters from the sp_cursor[prep]exec call.
        // There should not be any rows present when initializing a server-cursored
        // ResultSet (until/unless support for cursor auto-fetch is implemented).
        final class ServerCursorInitializer extends CursorInitializer {
            private final SQLServerStatement stmt;

            final int getRowCount() {
                return stmt.getServerCursorRowCount();
            }

            final int getServerCursorId() {
                return stmt.getServerCursorId();
            }

            ServerCursorInitializer(SQLServerStatement stmt) {
                super("ServerCursorInitializer");
                this.stmt = stmt;
            }

            boolean onRetStatus(TDSReader tdsReader) throws SQLServerException {
                // With server-cursored result sets, the column metadata is
                // followed by a return status and cursor-related OUT parameters
                // for the sp_cursor[prep]exec call. Two of those OUT parameters
                // are the cursor ID and row count needed to construct this
                // ResultSet.
                stmt.consumeExecOutParam(tdsReader);
                return true;
            }

            boolean onRetValue(TDSReader tdsReader) throws SQLServerException {
                // The first OUT parameter after the sp_cursor[prep]exec OUT parameters
                // is the start of the application OUT parameters. Leave parsing
                // of them up to CallableStatement OUT param handlers.
                return false;
            }
        }

        // Client-cursor initializer expects 0 or more rows or a row-level error
        // to follow the column metadata.
        final class ClientCursorInitializer extends CursorInitializer {
            private int rowCount = UNKNOWN_ROW_COUNT;

            final int getRowCount() {
                return rowCount;
            }

            final int getServerCursorId() {
                return 0;
            }

            ClientCursorInitializer() {
                super("ClientCursorInitializer");
            }

            boolean onRow(TDSReader tdsReader) throws SQLServerException {
                // A ROW token indicates the start of the fetch buffer
                return false;
            }

            boolean onNBCRow(TDSReader tdsReader) throws SQLServerException {
                // A NBCROW token indicates the start of the fetch buffer
                return false;
            }

            boolean onError(TDSReader tdsReader) throws SQLServerException {
                // An ERROR token indicates a row error in lieu of a row.
                // In this case, the row error is in lieu of the first row.
                // Stop parsing and let the fetch buffer handle the error.
                rowCount = 0;
                return false;
            }

            boolean onDone(TDSReader tdsReader) throws SQLServerException {
                // When initializing client-cursored ResultSets, a DONE token
                // following the column metadata indicates an empty result set.
                rowCount = 0;

                // Continue to read the error message if DONE packet has error flag
                int packetType = tdsReader.peekTokenType();
                if (TDS.TDS_DONE == packetType) {
                    short status = tdsReader.peekStatusFlag();
                    // check if status flag has DONE_ERROR set i.e., 0x2
                    if ((status & 0x0002) != 0) {
                        // Consume the DONE packet if there is error
                        StreamDone doneToken = new StreamDone();
                        doneToken.setFromTDS(tdsReader);
                        return true;
                    }
                }

                return false;
            }
        }

        this.stmt = stmtIn;
        this.maxRows = stmtIn.maxRows;
        this.fetchSize = stmtIn.nFetchSize;
        this.fetchDirection = stmtIn.nFetchDirection;

        CursorInitializer initializer = stmtIn.executedSqlDirectly ? (new ClientCursorInitializer()) : (new ServerCursorInitializer(stmtIn));

        TDSParser.parse(stmtIn.resultsReader(), initializer);
        this.columns = initializer.buildColumns();
        this.rowCount = initializer.getRowCount();
        this.serverCursorId = initializer.getServerCursorId();

        // If this result set does not use a server cursor, then the result set rows
        // (if any) are already present in the fetch buffer at which the statement's
        // TDSReader now points.
        //
        // If this result set uses a server cursor, then without support for server
        // cursor autofetch, there are initially no rows with which to populate the
        // fetch buffer. The app will have to do a server cursor fetch first.
        this.tdsReader = (0 == serverCursorId) ? stmtIn.resultsReader() : null;

        this.fetchBuffer = new FetchBuffer();

        this.scrollWindow = isForwardOnly() ? null : new ScrollWindow(fetchSize);
        this.numFetchedRows = 0;

        // increment opened resultset counter
        stmtIn.incrResultSetCount();

        if (logger.isLoggable(java.util.logging.Level.FINE)) {
            logger.fine(toString() + " created by (" + stmt.toString() + ")");
        }
    }

    public boolean isWrapperFor(Class iface) throws SQLException {
        loggerExternal.entering(getClassNameLogging(), "isWrapperFor");
        DriverJDBCVersion.checkSupportsJDBC4();
        boolean f = iface.isInstance(this);
        loggerExternal.exiting(getClassNameLogging(), "isWrapperFor", Boolean.valueOf(f));
        return f;
    }

    public  T unwrap(Class iface) throws SQLException {
        loggerExternal.entering(getClassNameLogging(), "unwrap");
        DriverJDBCVersion.checkSupportsJDBC4();
        T t;
        try {
            t = iface.cast(this);
        }
        catch (ClassCastException e) {
            throw new SQLServerException(e.getMessage(), e);
        }
        loggerExternal.exiting(getClassNameLogging(), "unwrap", t);
        return t;
    }

    private SQLServerException rowErrorException = null;

    /**
     * Check if the result set is closed
     * 
     * @throws SQLServerException
     */
    /* L0 */ void checkClosed() throws SQLServerException {

        if (isClosed) {
            SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_resultsetClosed"), null, false);
        }

        stmt.checkClosed();

        // This ResultSet isn't closed, but also check whether it's effectively dead
        // due to a row error. Once a ResultSet encounters a row error, nothing more
        // can be done with it other than closing it.
        if (null != rowErrorException)
            throw rowErrorException;
    }

    public boolean isClosed() throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();

        loggerExternal.entering(getClassNameLogging(), "isClosed");
        boolean result = isClosed || stmt.isClosed();
        loggerExternal.exiting(getClassNameLogging(), "isClosed", result);
        return result;
    }

    /**
     * Called by ResultSet API methods to disallow method use on forward only result sets.
     *
     * @throws SQLServerException
     *             if the result set is forward only.
     */
    private void throwNotScrollable() throws SQLServerException {
        SQLServerException.makeFromDriverError(stmt.connection, this, SQLServerException.getErrString("R_requestedOpNotSupportedOnForward"), null,
                true);
    }

    private boolean isForwardOnly() {
        return TYPE_SS_DIRECT_FORWARD_ONLY == stmt.getSQLResultSetType() || TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == stmt.getSQLResultSetType();
    }

    private boolean isDynamic() {
        return 0 != serverCursorId && TDS.SCROLLOPT_DYNAMIC == stmt.getCursorType();
    }

    private void verifyResultSetIsScrollable() throws SQLServerException {
        if (isForwardOnly())
            throwNotScrollable();
    }

    /**
     * Called by ResultSet API methods to disallow method use on read only result sets.
     *
     * @throws SQLServerException
     *             if the result set is read only.
     */
    private void throwNotUpdatable() throws SQLServerException {
        SQLServerException.makeFromDriverError(stmt.connection, this, SQLServerException.getErrString("R_resultsetNotUpdatable"), null, true);
    }

    private void verifyResultSetIsUpdatable() throws SQLServerException {
        if (CONCUR_READ_ONLY == stmt.resultSetConcurrency || 0 == serverCursorId)
            throwNotUpdatable();
    }

    /**
     * Checks whether the result set has a current row.
     *
     * @return true if there is a current row
     * @return false if the result set is positioned before the first row or after the last row.
     */
    private boolean hasCurrentRow() {
        return BEFORE_FIRST_ROW != currentRow && AFTER_LAST_ROW != currentRow;
    }

    /**
     * Verifies whether this result set has a current row.
     *
     * This check DOES NOT consider whether the cursor is on the insert row. The result set may or may not have a current row regardless whether the
     * cursor is on the insert row. Consider the following scenarios:
     *
     * beforeFirst(); moveToInsertRow(); relative(1); No current row to move relative to. Throw "no current row" exception.
     *
     * first(); moveToInsertRow(); relative(1); Call to relative moves off of the insert row one row past the current row. That is, the cursor ends up
     * on the second row of the result set.
     *
     * @throws SQLServerException
     *             if the result set has no current row
     */
    private void verifyResultSetHasCurrentRow() throws SQLServerException {
        if (!hasCurrentRow()) {
            SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_resultsetNoCurrentRow"), null, true);
        }
    }

    /**
     * Called by ResultSet API methods to disallow method use when cursor is on a deleted row.
     *
     * @throws SQLServerException
     *             if the cursor is not on an updatable row.
     */
    private void verifyCurrentRowIsNotDeleted(String errResource) throws SQLServerException {
        if (currentRowDeleted()) {
            SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString(errResource), null, true);
        }
    }

    /**
     * Called by ResultSet API methods to disallow method use when the column index is not in the range of columns returned in the results.
     *
     * @throws SQLServerException
     *             if the column index is out of bounds
     */
    private void verifyValidColumnIndex(int index) throws SQLServerException {
        int nCols = columns.length;

        // Rows that come back from server side cursors tack on a "hidden" ROWSTAT column
        // (used to detect deletes from a keyset) at the end of each row. Don't include
        // that column in the list of valid columns.
        if (0 != serverCursorId)
            --nCols;

        if (index < 1 || index > nCols) {
            MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_indexOutOfRange"));
            Object[] msgArgs = {new Integer(index)};
            SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), "07009", false);
        }
    }

    /**
     * Called by ResultSet API methods to disallow method use when cursor is on the insert row.
     *
     * @throws SQLServerException
     *             if the cursor is on the insert row.
     */
    private void verifyResultSetIsNotOnInsertRow() throws SQLServerException {
        if (isOnInsertRow) {
            SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_mustNotBeOnInsertRow"), null, true);
        }
    }

    private void throwUnsupportedCursorOp() throws SQLServerException {
        // Absolute positioning of dynamic cursors is unsupported.
        SQLServerException.makeFromDriverError(stmt.connection, this, SQLServerException.getErrString("R_unsupportedCursorOperation"), null, true);
    }

    /**
     * Close the result set.
     *
     * Note that the public close() method performs all of the cleanup work through this internal method which cannot throw any exceptions. This is
     * done deliberately to ensure that ALL of the object's client-side and server-side state is cleaned up as best as possible, even under conditions
     * which would normally result in exceptions being thrown.
     */
    private void closeInternal() {
        // Calling close on a closed ResultSet is a no-op per JDBC spec
        if (isClosed)
            return;

        // Mark this ResultSet as closed, then clean up.
        isClosed = true;

        // Discard the current fetch buffer contents.
        discardFetchBuffer();

        // Close the server cursor if there is one.
        closeServerCursor();

        // Clean up client-side state
        metaData = null;

        // decrement opened resultset counter
        stmt.decrResultSetCount();
    }

    public void close() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "close");
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        closeInternal();
        loggerExternal.exiting(getClassNameLogging(), "close");
    }

    /**
     * Find a column index given a column name
     * 
     * @param columnName
     *            the name of the column
     * @throws SQLServerException
     *             If any errors occur.
     * @return the column index
     */
    public int findColumn(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "findColumn", columnName);
        checkClosed();

        // In order to be as accurate as possible when locating column name
        // indexes, as well as be deterministic when running on various client
        // locales, we search for column names using the following scheme:

        // Per JDBC spec 27.1.5 "if there are multiple columns with the same name
        // [findColumn] will return the value of the first matching name".

        // 1. Search using case-sensitive non-locale specific (binary) compare first.
        // 2. Search using case-insensitive, non-locale specific (binary) compare last.

        // NOTE: Any attempt to use a locale aware comparison will fail because:
        //
        // 1. SQL allows any valid UNICODE characters in the column name.
        // 2. SQL does not store any locale info associated with the column name.
        // 3. We cannot second guess the developer and decide to use VM locale or
        // database default locale when making comparisons, this would produce
        // inconsistent results on different clients or different servers.

        // Search using case-sensitive, non-locale specific (binary) compare.
        // If the user supplies a true match for the column name, we will find it here.
        int i;
        for (i = 0; i < columns.length; i++) {
            if (columns[i].getColumnName().equals(columnName)) {
                loggerExternal.exiting(getClassNameLogging(), "findColumn", i + 1);
                return i + 1;
            }
        }

        // Check for case-insensitive match using a non-locale aware method.
        // Per JDBC spec, 27.3 "The driver will do a case-insensitive search for
        // columnName in it's attempt to map it to the column's index".
        // Use VM supplied String.equalsIgnoreCase to do the "case-insensitive search".
        for (i = 0; i < columns.length; i++) {
            if (columns[i].getColumnName().equalsIgnoreCase(columnName)) {
                loggerExternal.exiting(getClassNameLogging(), "findColumn", i + 1);
                return i + 1;
            }
        }
        MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnName"));
        Object[] msgArgs = {columnName};
        SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), "07009", false);

        return 0;
    }

    final int getColumnCount() {
        int nCols = columns.length;
        if (0 != serverCursorId)
            nCols--; // Do not include SQL Server's automatic rowstat column
        return nCols;
    }

    final Column getColumn(int columnIndex) throws SQLServerException {
        // Close any stream that might be open on the current column
        // before moving to another one.
        if (null != activeStream) {
            try {
                activeStream.close();
            }
            catch (IOException e) {
                SQLServerException.makeFromDriverError(null, null, e.getMessage(), null, true);
            }
            finally {
                activeStream = null;
            }
        }

        return columns[columnIndex - 1];
    }

    /**
     * This function initializes null compressed columns only when the row type is NBCROW and if the areNullCompressedColumnsInitialized is false. In
     * all other cases this will be a no-op.
     * 
     * @throws SQLServerException
     */
    private void initializeNullCompressedColumns() throws SQLServerException {
        if (resultSetCurrentRowType.equals(RowType.NBCROW) && (!areNullCompressedColumnsInitialized)) {
            int columnNo = 0;
            // no of bytes to be read from the stream
            int noOfBytes = ((this.columns.length - 1) >> 3) + 1;// equivalent of (int)Math.ceil(this.columns.length/8.0) and gives better perf
            for (int byteNo = 0; byteNo < noOfBytes; byteNo++) {

                int byteValue = tdsReader.readUnsignedByte();

                // if this byte is 0, skip to the next byte
                // and increment the column number by 8(no of bits)
                if (byteValue == 0) {
                    columnNo = columnNo + 8;
                    continue;
                }

                for (int bitNo = 0; bitNo < 8 && columnNo < this.columns.length; bitNo++) {
                    if ((byteValue & (1 << bitNo)) != 0) {
                        this.columns[columnNo].initFromCompressedNull();
                    }
                    columnNo++;
                }
            }
            areNullCompressedColumnsInitialized = true;
        }
    }

    private Column loadColumn(int index) throws SQLServerException {
        assert 1 <= index && index <= columns.length;

        initializeNullCompressedColumns();

        // Skip any columns between the last indexed column and the target column,
        // retaining their values so they can be retrieved later.
        if (index > lastColumnIndex && (!this.columns[index - 1].isInitialized()))
            skipColumns(index - lastColumnIndex, false);

        // Then return the target column
        return getColumn(index);
    }

    /* L0 */ private void NotImplemented() throws SQLServerException {
        SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_notSupported"), null, false);
    }

    /**
     * Clear result set warnings
     * 
     * @throws SQLServerException
     *             when an error occurs
     */
    /* L0 */ public void clearWarnings() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "clearWarnings");
        loggerExternal.exiting(getClassNameLogging(), "clearWarnings");
    }

    /* ----------------- JDBC API methods ------------------ */

    private void moverInit() throws SQLServerException {
        cancelInsert();
        cancelUpdates();
    }

    public boolean relative(int rows) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "relative", rows);

        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + " rows:" + rows + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if (1) there is no curent row or (2)
        // the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();
        verifyResultSetHasCurrentRow();

        moverInit();
        moveRelative(rows);
        boolean value = hasCurrentRow();
        loggerExternal.exiting(getClassNameLogging(), "relative", value);
        return value;
    }

    private void moveRelative(int rowsToMove) throws SQLServerException {
        // Relative moves must be from somewhere within the result set
        assert hasCurrentRow();

        // If rows is 0, the cursor's position does not change.
        if (0 == rowsToMove)
            return;

        if (rowsToMove > 0)
            moveForward(rowsToMove);
        else
            moveBackward(rowsToMove);
    }

    private void moveForward(int rowsToMove) throws SQLServerException {
        assert hasCurrentRow();
        assert rowsToMove > 0;

        // If there's a chance that the move can happen just in the scroll window then try that first
        if (scrollWindow.getRow() + rowsToMove <= scrollWindow.getMaxRows()) {
            int rowsMoved = 0;
            while (rowsToMove > 0 && scrollWindow.next(this)) {
                ++rowsMoved;
                --rowsToMove;
            }

            // Update the current row
            updateCurrentRow(rowsMoved);

            // If the move happened entirely in the scroll window, then we're done.
            if (0 == rowsToMove)
                return;
        }

        // All or part of the move lies outside the scroll window.
        assert rowsToMove > 0;

        // For client-cursored result sets, where the fetch buffer contains all of the rows, moves outside of
        // the scroll window are done via an absolute in the fetch buffer.
        if (0 == serverCursorId) {
            assert UNKNOWN_ROW != currentRow;
            currentRow = clientMoveAbsolute(currentRow + rowsToMove);
            return;
        }

        // For server-cursored result sets (where the fetch buffer and scroll window are the same size),
        // moves outside the scroll window require fetching more rows from the server.
        //
        // A few words on fetching strategy with server cursors
        // There is an assumption here that moving past the current position is an indication
        // that the result set is being traversed in forward order, so it makes sense to grab a
        // block of fetchSize rows from the server, starting at the desired location, to maximize
        // the number of rows that can be consumed before the next server fetch. That assumption
        // isn't necessarily true.
        if (1 == rowsToMove)
            doServerFetch(TDS.FETCH_NEXT, 0, fetchSize);
        else
            doServerFetch(TDS.FETCH_RELATIVE, rowsToMove + scrollWindow.getRow() - 1, fetchSize);

        // If the new fetch buffer returned no rows, then the cursor has reached the end of the result set.
        if (!scrollWindow.next(this)) {
            currentRow = AFTER_LAST_ROW;
            return;
        }

        // The move succeeded, so update the current row.
        updateCurrentRow(rowsToMove);
    }

    private void moveBackward(int rowsToMove) throws SQLServerException {
        assert hasCurrentRow();
        assert rowsToMove < 0;

        // If the move is contained in scroll window then handle it there.
        if (scrollWindow.getRow() + rowsToMove >= 1) {
            for (int rowsMoved = 0; rowsMoved > rowsToMove; --rowsMoved)
                scrollWindow.previous(this);

            updateCurrentRow(rowsToMove);
            return;
        }

        // The move lies outside the scroll window.

        // For client-cursored result sets, where the fetch buffer contains all of the rows, moves outside of
        // the scroll window are done via an absolute move in the fetch buffer.
        if (0 == serverCursorId) {
            assert UNKNOWN_ROW != currentRow;

            // Relative moves to before the first row must be handled here; a negative argument
            // to clientMoveAbsolute is interpreted as relative to the last row, not the first.
            if (currentRow + rowsToMove < 1) {
                moveBeforeFirst();
            }
            else {
                currentRow = clientMoveAbsolute(currentRow + rowsToMove);
            }

            return;
        }

        // For server-cursored result sets (where the fetch buffer and scroll window are the same size),
        // moves outside the scroll window require fetching more rows from the server.
        //
        // A few words on fetching strategy with server cursors
        // There is an assumption here that moving to the previous row is an indication
        // that the result set is being traversed in reverse order, so it makes sense to grab a
        // block of fetchSize rows from the server, ending with the desired location, to maximize
        // the number of rows that can be consumed before the next server fetch. That assumption
        // isn't necessarily true.
        //
        // Also, when moving further back than the previous row, it is not generally feasible to
        // try to fetch a block of rows ending with the target row, since a move far enough back
        // that the start of the fetch buffer would be before the first row of the result set would
        // position the cursor before the first row and return no rows, even though the target row
        // may not be before the first row. Instead, such moves are done so that the target row
        // is the first row in the returned block of rows rather than the last row.
        if (-1 == rowsToMove) {
            doServerFetch(TDS.FETCH_PREV_NOADJUST, 0, fetchSize);

            // If the new fetch buffer returned no rows, then the cursor has reached the start of the result set.
            if (!scrollWindow.next(this)) {
                currentRow = BEFORE_FIRST_ROW;
                return;
            }

            // Scroll past the last of the returned rows, and ...
            while (scrollWindow.next(this))
                ;

            // back up one row.
            scrollWindow.previous(this);
        }
        else {
            doServerFetch(TDS.FETCH_RELATIVE, rowsToMove + scrollWindow.getRow() - 1, fetchSize);

            // If the new fetch buffer returned no rows, then the cursor has reached the start of the result set.
            if (!scrollWindow.next(this)) {
                currentRow = BEFORE_FIRST_ROW;
                return;
            }
        }

        // The move succeeded, so update the current row.
        updateCurrentRow(rowsToMove);
    }

    /**
     * Update the current row's position if known.
     *
     * If known, the current row is assumed to be at a valid position somewhere in the ResultSet. That is, the current row is not before the first row
     * or after the last row.
     */
    private void updateCurrentRow(int rowsToMove) {
        if (UNKNOWN_ROW != currentRow) {
            assert currentRow >= 1;
            currentRow += rowsToMove;
            assert currentRow >= 1;
        }
    }

    /**
     * Initially moves the cursor to the first row of this ResultSet object, with subsequent calls moving the cursor to the second row, the third row,
     * and so on.
     *
     * @return false when there are no more rows to read
     * @return true otherwise
     */
    public boolean next() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "next");
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        moverInit();

        // If the cursor is already positioned after the last row in this result set
        // then it can't move any farther forward.
        if (AFTER_LAST_ROW == currentRow) {
            loggerExternal.exiting(getClassNameLogging(), "next", false);
            return false;
        }

        // For scrollable cursors, next() is just a special case of relative()
        if (!isForwardOnly()) {
            if (BEFORE_FIRST_ROW == currentRow)
                moveFirst();
            else
                moveForward(1);
            boolean value = hasCurrentRow();
            loggerExternal.exiting(getClassNameLogging(), "next", value);
            return value;
        }

        // Fast path for forward only cursors...

        // Server forward only cursors do not honor SET ROWCOUNT,
        // so enforce any maxRows limit here.
        if (0 != serverCursorId && maxRows > 0) {
            if (currentRow == maxRows) {
                currentRow = AFTER_LAST_ROW;
                loggerExternal.exiting(getClassNameLogging(), "next", false);
                return false;
            }
        }

        // There is no scroll window for forward only cursors,
        // so try to get the next row directly from the fetch buffer.
        if (fetchBufferNext()) {
            // Update the current row.
            // Note that if the position was before the first row, the current
            // row should be updated to row 1.
            if (BEFORE_FIRST_ROW == currentRow)
                currentRow = 1;
            else
                updateCurrentRow(1);

            // We should never be asked to read more rows than maxRows.
            // Server forward only is handled above, and maxRows should
            // be enforced by the server for DIRECT forward only cursors.
            assert 0 == maxRows || currentRow <= maxRows;
            loggerExternal.exiting(getClassNameLogging(), "next", true);
            // Return that a row was read.
            return true;
        }

        // We're out of rows in the fetch buffer. If this is a server
        // cursor, then try to load up the fetch buffer with the next
        // set of fetchSize rows.
        if (0 != serverCursorId) {
            doServerFetch(TDS.FETCH_NEXT, 0, fetchSize);

            // If there are rows in the freshly-loaded fetch buffer
            // then return the first of them.
            if (fetchBufferNext()) {
                if (BEFORE_FIRST_ROW == currentRow)
                    currentRow = 1;
                else
                    updateCurrentRow(1);

                assert 0 == maxRows || currentRow <= maxRows;
                loggerExternal.exiting(getClassNameLogging(), "next", true);
                return true;
            }
        }

        // Otherwise, we have reached the end of the result set
        if (UNKNOWN_ROW_COUNT == rowCount)
            rowCount = currentRow;

        currentRow = AFTER_LAST_ROW;
        loggerExternal.exiting(getClassNameLogging(), "next", false);
        return false;
    }

    public boolean wasNull() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "wasNull");
        checkClosed();
        loggerExternal.exiting(getClassNameLogging(), "wasNull", lastValueWasNull);
        return lastValueWasNull;
    }

    /**
     * @return true if the cursor is before the first row in this result set
     * @return false otherwise or if thie result set contains no rows.
     */
    public boolean isBeforeFirst() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "isBeforeFirst");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        //
        // We deviate from JDBC spec here and allow this call on scrollable result sets.
        // Other drivers do the same. Hibernate requires this behavior.
        // verifyResultSetIsScrollable();

        if (0 != serverCursorId) {
            switch (stmt.getCursorType()) {
                case TDS.SCROLLOPT_FORWARD_ONLY:
                    throwNotScrollable();
                    break;

                case TDS.SCROLLOPT_DYNAMIC:
                    throwUnsupportedCursorOp();
                    break;

                case TDS.SCROLLOPT_FAST_FORWARD:
                    throwNotScrollable();
                    break;

                // All other types (KEYSET, STATIC) return a row count up front
                default:
                    break;
            }
        }

        if (isOnInsertRow)
            return false;

        // If the cursor is not positioned before the first row in this result set
        // then the answer is obvious:
        if (BEFORE_FIRST_ROW != currentRow)
            return false;

        // If the cursor is positioned before the first row in this result set then
        // isBeforeFirst returns true only if the result set is also not empty.

        // For client-cursored result sets, determining whether the result set is empty
        // is just a matter of checking whether there are rows in the fetch buffer.
        if (0 == serverCursorId)
            return fetchBufferHasRows();

        // For server-cursored result sets, the row count tells whether the result set
        // is empty. Assumption: server cursors that do not provide a row count (e.g. DYNAMIC)
        // are handled above.
        assert rowCount >= 0;
        boolean value = rowCount > 0;
        loggerExternal.exiting(getClassNameLogging(), "isBeforeFirst", value);
        return value;
    }

    public boolean isAfterLast() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "isAfterLast");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        //
        // We deviate from JDBC spec here and allow this call on forward only client-cursored
        // result sets. Other drivers do the same. Hibernate requires this behavior.
        if (0 != serverCursorId) {
            verifyResultSetIsScrollable();

            // Scrollable DYNAMIC cursors do not support isAfterLast() since they
            // don't provide a row count and cannot distinguish an empty fetch
            // buffer from an empty result set.
            if (TDS.SCROLLOPT_DYNAMIC == stmt.getCursorType() && !isForwardOnly())
                throwUnsupportedCursorOp();
        }

        if (isOnInsertRow)
            return false;

        // By the time the cursor is positioned after the last row of the result set,
        // the count of rows must be known.
        assert !(AFTER_LAST_ROW == currentRow && UNKNOWN_ROW_COUNT == rowCount);

        boolean value = AFTER_LAST_ROW == currentRow && rowCount > 0;
        loggerExternal.exiting(getClassNameLogging(), "isAfterLast", value);
        return value;
    }

    /**
     * Determines whether the cursor is on the first row in this ResultSet object.
     *
     * This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE,
     * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
     *
     * @return true if the cursor is on the first row in this result set
     * @return false otherwise
     */
    public boolean isFirst() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "isFirst");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        // DYNAMIC cursors do not support isFirst(). There is no way to determine absolute
        // position within the result set with a DYNAMIC cursor.
        if (isDynamic())
            throwUnsupportedCursorOp();

        if (isOnInsertRow)
            return false;

        // At this point we must have a cursor that is scrollable and non-DYNAMIC.
        // That is, we have a cursor that has a notion of absolute position.
        assert UNKNOWN_ROW != currentRow;

        // Just return whether that absolution position is the first row.
        boolean value = 1 == currentRow;
        loggerExternal.exiting(getClassNameLogging(), "isFirst", value);
        return value;
    }

    /**
     * Determines whether the cursor is on the last row in this ResultSet object.
     *
     * This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE,
     * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
     *
     * @return true if the cursor is on the last row in this result set
     * @return false otherwise
     */
    public boolean isLast() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "isLast");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        // DYNAMIC cursors do not support isLast(). There is no way to determine absolute
        // position within the result set with a DYNAMIC cursor.
        if (isDynamic())
            throwUnsupportedCursorOp();

        if (isOnInsertRow)
            return false;

        // If the cursor is before the first row or after the last row then
        // it is by definition not on the last row.
        if (!hasCurrentRow())
            return false;

        // At this point circumstances are such that we must know the current row
        assert currentRow >= 1;

        // Determining whether the current row is the last row is easy if we know the row count
        if (UNKNOWN_ROW_COUNT != rowCount) {
            assert currentRow <= rowCount;
            return currentRow == rowCount;
        }

        // If we don't know the row count, determining whether the current row is
        // the last row is not quite as straightforward, but still reasonably efficient.
        // Presumably since we've ruled out a DYNAMIC cursor, the only way we would
        // not know the row count at this point is if we have a client cursor.
        assert 0 == serverCursorId;

        // All server cursors other than DYNAMIC give us a row count. If we have
        // a client cursor, then we can tell whether the current row is the last
        // row just by checking whether there are any more rows in the fetch buffer.
        // A call to isLast() logically should not modify the current position in
        // the response, so save the current position and restore it on exit.
        boolean isLast = !next();
        previous();
        loggerExternal.exiting(getClassNameLogging(), "isLast", isLast);
        return isLast;
    }

    public void beforeFirst() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "beforeFirst");
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        moverInit();
        moveBeforeFirst();
        loggerExternal.exiting(getClassNameLogging(), "beforeFirst");
    }

    private void moveBeforeFirst() throws SQLServerException {
        if (0 == serverCursorId) {
            fetchBufferBeforeFirst();
            scrollWindow.clear();
        }
        else {
            doServerFetch(TDS.FETCH_FIRST, 0, 0);
        }

        currentRow = BEFORE_FIRST_ROW;
    }

    public void afterLast() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "afterLast");
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }

        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        moverInit();
        moveAfterLast();
        loggerExternal.exiting(getClassNameLogging(), "afterLast");
    }

    private void moveAfterLast() throws SQLServerException {
        assert !isForwardOnly();

        if (0 == serverCursorId)
            clientMoveAfterLast();
        else
            doServerFetch(TDS.FETCH_LAST, 0, 0);

        currentRow = AFTER_LAST_ROW;
    }

    /**
     * Moves the cursor to the first row in this ResultSet object.
     *
     * This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE,
     * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
     *
     * @return true if the cursor is on a valid row
     * @return false if there are no rows in this ResultSet object
     */
    public boolean first() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "first");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        moverInit();
        moveFirst();
        boolean value = hasCurrentRow();
        loggerExternal.exiting(getClassNameLogging(), "first", value);
        return value;
    }

    private void moveFirst() throws SQLServerException {
        if (0 == serverCursorId) {
            moveBeforeFirst();
        }
        else {
            // Fetch the first block of up to fetchSize rows
            doServerFetch(TDS.FETCH_FIRST, 0, fetchSize);
        }

        // Start the scroll window at the first row in the fetch buffer
        if (!scrollWindow.next(this)) {
            // If there are no rows in the result set then just ensure the current row
            // is positioned at a consistent location so that subsequent ResultSet
            // operations behave consistently.
            //
            // The actual position of the server cursor does not matter in this case.
            currentRow = AFTER_LAST_ROW;
            return;
        }

        // Adjust the current row appropriately
        currentRow = isDynamic() ? UNKNOWN_ROW : 1;
    }

    /**
     * Moves the cursor to the last row in this ResultSet object.
     *
     * This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE,
     * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
     *
     * @return true if the cursor is on a valid row
     * @return false if there are no rows in this ResultSet object
     */
    public boolean last() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "last");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        moverInit();
        moveLast();
        boolean value = hasCurrentRow();
        loggerExternal.exiting(getClassNameLogging(), "last", value);
        return value;
    }

    private void moveLast() throws SQLServerException {
        if (0 == serverCursorId) {
            currentRow = clientMoveAbsolute(-1);
            return;
        }

        // Fetch the last block of up to fetchSize rows from the result set
        doServerFetch(TDS.FETCH_LAST, 0, fetchSize);

        // Start the scroll window at the first row in the fetch buffer
        if (!scrollWindow.next(this)) {
            // If there are no rows in the result set then just ensure the current row
            // is positioned at a consistent location so that subsequent ResultSet
            // operations behave consistently.
            //
            // The actual position of the server cursor does not matter in this case.
            currentRow = AFTER_LAST_ROW;
            return;
        }

        // Scroll to the last of the returned rows
        while (scrollWindow.next(this))
            ;
        scrollWindow.previous(this);

        // Adjust the current row appropriately
        currentRow = isDynamic() ? UNKNOWN_ROW : rowCount;
    }

    /**
     * Retrieves the number of the current row in this ResultSet object. The first row is number 1, the second is 2, and so on.
     *
     * @return the number of the current row; 0 if there is no current row
     */
    public int getRow() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getRow");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // DYNAMIC (scrollable) cursors do not support getRow() since they do not have any
        // concept of absolute position.
        if (isDynamic() && !isForwardOnly())
            throwUnsupportedCursorOp();

        // From JDBC spec:
        // [returns] 0 if there is no current row.
        //
        // From our ResultSet Cursors feature spec:
        // getRow returns 0 when in insert mode.
        if (!hasCurrentRow() || isOnInsertRow)
            return 0;

        // We should be dealing with a cursor type that has a notion of absolute position
        assert currentRow >= 1;

        // Return that absolute position
        loggerExternal.exiting(getClassNameLogging(), "getRow", currentRow);
        return currentRow;
    }

    /**
     * Moves the cursor to the specified row in this ResultSet object. The specified row may be positive, negative or zero.
     *
     * This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE,
     * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
     *
     * @return true if the cursor is on a valid row in this result set
     * @return false if the cursor is before the first row or after the last row
     */
    public boolean absolute(int row) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "absolute");
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + " row:" + row + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        // DYNAMIC cursors do not support absolute(). There is no way to determine absolute
        // position within the result set with a DYNAMIC cursor.
        if (isDynamic())
            throwUnsupportedCursorOp();

        moverInit();
        moveAbsolute(row);
        boolean value = hasCurrentRow();
        loggerExternal.exiting(getClassNameLogging(), "absolute", value);
        return value;
    }

    private void moveAbsolute(int row) throws SQLServerException {
        // Absolute positioning is not allowed for cursor types that don't support
        // knowing the absolute position.
        assert UNKNOWN_ROW != currentRow;
        assert !isDynamic();

        switch (row) {
            // If row is 0, the cursor is positioned before the first row.
            case 0:
                moveBeforeFirst();
                return;

            // Calling absolute(1) is the same as calling the method first().
            case 1:
                moveFirst();
                return;

            // Calling absolute(-1) is the same as calling the method last().
            case -1:
                moveLast();
                return;
        }

        // Depending on how much we know about the result set, an absolute move
        // can be translated into a relative move. The advantage to doing this
        // is that we gain the benefit of using the scroll window, which reduces
        // calls to the server when absolute moves can translate to small moves
        // relative to the current row.
        if (hasCurrentRow()) {
            assert currentRow >= 1;

            // If the absolute move is from the start of the result set (+ve rows)
            // then we can easily express it as a relative (to the current row) move:
            // the amount to move is just the difference between the current row and
            // the target absolute row.
            if (row > 0) {
                moveRelative(row - currentRow);
                return;
            }

            // If the absolute move is from the end of the result set (-ve rows)
            // then we also need to know how many rows are in the result set.
            // If we do then we can convert to an absolute move from the start
            // of the result set, and apply the logic above.
            if (UNKNOWN_ROW_COUNT != rowCount) {
                assert row < 0;
                moveRelative((rowCount + row + 1) - currentRow);
                return;
            }
        }

        // Ok, so there's no chance of a relative move. In other words, the current
        // position may be before the first row or after the last row. Or perhaps
        // it's an absolute move from the end of the result set and we don't know
        // how many rows there are yet (can happen with a scrollable client cursor).
        // In that case, we need to move absolutely.

        // Try to fetch a block of up to fetchSize rows starting at row row.
        if (0 == serverCursorId) {
            currentRow = clientMoveAbsolute(row);
            return;
        }

        doServerFetch(TDS.FETCH_ABSOLUTE, row, fetchSize);

        // If the absolute server fetch didn't land somewhere on the result set
        // then it's either before the first row or after the last row.
        if (!scrollWindow.next(this)) {
            currentRow = (row < 0) ? BEFORE_FIRST_ROW : AFTER_LAST_ROW;
            return;
        }

        // The absolute server fetch landed somewhere on the result set,
        // so update the current row to reflect the new position.
        if (row > 0) {
            // The current row is just the row to which we moved.
            currentRow = row;
        }
        else {
            // Absolute fetch with -ve row is relative to the end of the result set.
            assert row < 0;
            assert rowCount + row + 1 >= 1;
            currentRow = rowCount + row + 1;
        }
    }

    private boolean fetchBufferHasRows() throws SQLServerException {
        // Never call this with server cursors without first determining whether the
        // fetch buffer exists yet.
        assert 0 == serverCursorId;
        assert null != tdsReader;

        assert lastColumnIndex >= 0;

        // If we're somewhere in the middle of a row, then obviously the fetch buffer has rows!
        if (lastColumnIndex >= 1)
            return true;

        // We're not somewhere in the middle of a row, so we're either at the start of a row
        // or looking at an empty fetch buffer. Peeking at the next TDS token tells us which.
        int tdsTokenType = tdsReader.peekTokenType();

        // Return whether the next item in the response appears to be a row or something
        // that should have been a row.
        return (TDS.TDS_ROW == tdsTokenType || TDS.TDS_NBCROW == tdsTokenType || TDS.TDS_MSG == tdsTokenType || TDS.TDS_ERR == tdsTokenType);
    }

    final void discardCurrentRow() throws SQLServerException {
        assert lastColumnIndex >= 0;

        updatedCurrentRow = false;
        deletedCurrentRow = false;
        if (lastColumnIndex >= 1) {
            initializeNullCompressedColumns();
            // Discard columns up to, but not including, the last indexed column
            for (int columnIndex = 1; columnIndex < lastColumnIndex; ++columnIndex)
                getColumn(columnIndex).clear();

            // Skip and discard the remainder of the last indexed column
            // and all subsequent columns
            skipColumns(columns.length + 1 - lastColumnIndex, true);
        }

        // reset areNullCompressedColumnsInitialized to false and row type to unknown
        resultSetCurrentRowType = RowType.UNKNOWN;
        areNullCompressedColumnsInitialized = false;
    }

    final int fetchBufferGetRow() {
        if (isForwardOnly())
            return numFetchedRows;

        return scrollWindow.getRow();
    }

    final void fetchBufferBeforeFirst() throws SQLServerException {
        // Never call this with server cursors without first determining whether the
        // fetch buffer exists yet.
        assert 0 == serverCursorId;
        assert null != tdsReader;

        discardCurrentRow();

        fetchBuffer.reset();
        lastColumnIndex = 0;
    }

    final TDSReaderMark fetchBufferMark() {
        // Never call this if we don't have a fetch buffer...
        assert null != tdsReader;

        return tdsReader.mark();
    }

    final void fetchBufferReset(TDSReaderMark mark) throws SQLServerException {
        // Never call this if we don't have a fetch buffer...
        assert null != tdsReader;

        assert null != mark;

        discardCurrentRow();

        tdsReader.reset(mark);

        lastColumnIndex = 1;
    }

    final boolean fetchBufferNext() throws SQLServerException {
        // If we don't have a fetch buffer yet then there are no rows to fetch
        if (null == tdsReader)
            return false;

        // We do have a fetch buffer. So discard the current row in the fetch buffer and ...
        discardCurrentRow();

        // ... scan for the next row.
        // If we didn't find one, then we're done.
        RowType fetchBufferCurrentRowType = RowType.UNKNOWN;
        try {
            fetchBufferCurrentRowType = fetchBuffer.nextRow();
            if (fetchBufferCurrentRowType.equals(RowType.UNKNOWN))
                return false;
        }
        catch (SQLServerException e) {
            currentRow = AFTER_LAST_ROW;
            rowErrorException = e;
            throw e;
        }
        finally {
            lastColumnIndex = 0;
            resultSetCurrentRowType = fetchBufferCurrentRowType;
        }

        // Otherwise, we found a row.
        ++numFetchedRows;
        lastColumnIndex = 1;
        return true;
    }

    private void clientMoveAfterLast() throws SQLServerException {
        assert UNKNOWN_ROW != currentRow;

        int rowsSkipped = 0;
        while (fetchBufferNext())
            ++rowsSkipped;

        if (UNKNOWN_ROW_COUNT == rowCount) {
            assert AFTER_LAST_ROW != currentRow;
            rowCount = ((BEFORE_FIRST_ROW == currentRow) ? 0 : currentRow) + rowsSkipped;
        }
    }

    private int clientMoveAbsolute(int row) throws SQLServerException {
        assert 0 == serverCursorId;

        scrollWindow.clear();

        // Because there is no way to know how far from the end of the result
        // set we are, if we are asked to move some number of rows from the end
        // of the result set then we must translate that into some number of rows
        // to move from the start (i.e. convert a negative row move to a positive
        // row move).
        if (row < 0) {
            // In order to convert a negative move to an absolute positive move,
            // we need to know the number of rows in the result set. If we don't
            // already know the row count, then moving after the last row is
            // the only way to compute it (as a side effect).
            if (UNKNOWN_ROW_COUNT == rowCount) {
                clientMoveAfterLast();
                currentRow = AFTER_LAST_ROW;
            }

            // Now that we know the row count, we can translate the negative
            // move into a positive one.
            assert rowCount >= 0;

            // If we are moving backward from the end more rows than are in the
            // result set, then the ultimate position ends up before the first row.
            if (rowCount + row < 0) {
                moveBeforeFirst();
                return BEFORE_FIRST_ROW;
            }

            row = rowCount + row + 1;
        }

        // At this point we were either given a positive row movement from
        // the start of the result set or we have converted the negative row
        // movement that we were given from the end of the result set into a
        // positive row movememnt.
        assert row > 0;

        // If the target row lies somewhere before the current row (including the
        // current row itself, because moving to the current row moves back to
        // the _beginning_ of the current row), then we have to move all the way
        // back to the beginning of the result set, and then move from there.
        if (AFTER_LAST_ROW == currentRow || row <= currentRow)
            moveBeforeFirst();

        // Now move from the current row (which may be before the first row)
        // to the target row.
        assert BEFORE_FIRST_ROW == currentRow || currentRow < row;
        while (currentRow != row) {
            if (!fetchBufferNext()) {
                if (UNKNOWN_ROW_COUNT == rowCount)
                    rowCount = currentRow;
                return AFTER_LAST_ROW;
            }

            // Update the current row.
            // Note that if the position was before the first row, the current
            // row should be updated to row 1.
            if (BEFORE_FIRST_ROW == currentRow)
                currentRow = 1;
            else
                updateCurrentRow(1);
        }

        return row;
    }

    /**
     * Moves the cursor to the previous row in this ResultSet object.
     *
     * This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE,
     * TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
     *
     * @return true if the cursor is on a valid row in this result set
     * @return false otherwise
     */
    public boolean previous() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "previous");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        moverInit();

        if (BEFORE_FIRST_ROW == currentRow)
            return false;

        if (AFTER_LAST_ROW == currentRow)
            moveLast();
        else
            moveBackward(-1);

        boolean value = hasCurrentRow();
        loggerExternal.exiting(getClassNameLogging(), "previous", value);
        return value;
    }

    private void cancelInsert() {
        if (isOnInsertRow) {
            isOnInsertRow = false;
            clearColumnsValues();
        }
    }

    /** Clear any updated column values for the current row in the result set. */
    final void clearColumnsValues() {
        int l = columns.length;
        for (int i = 0; i < l; i++)
            columns[i].cancelUpdates();
    }

    /* L0 */ public SQLWarning getWarnings() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getWarnings");
        loggerExternal.exiting(getClassNameLogging(), "getWarnings", null);
        return null;
    }

    public void setFetchDirection(int direction) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "setFetchDirection", direction);
        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the type of this ResultSet object is TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        if ((ResultSet.FETCH_FORWARD != direction && ResultSet.FETCH_REVERSE != direction && ResultSet.FETCH_UNKNOWN != direction) ||

                (ResultSet.FETCH_FORWARD != direction && (SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY == stmt.resultSetType
                        || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == stmt.resultSetType))) {
            MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidFetchDirection"));
            Object[] msgArgs = {new Integer(direction)};
            SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), null, false);
        }

        fetchDirection = direction;
        loggerExternal.exiting(getClassNameLogging(), "setFetchDirection");
    }

    public int getFetchDirection() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getFetchDirection");
        checkClosed();
        loggerExternal.exiting(getClassNameLogging(), "getFetchDirection", fetchDirection);
        return fetchDirection;
    }

    public void setFetchSize(int rows) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "setFetchSize", rows);
        checkClosed();
        if (rows < 0)
            SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_invalidFetchSize"), null, false);

        fetchSize = (0 == rows) ? stmt.defaultFetchSize : rows;
        loggerExternal.exiting(getClassNameLogging(), "setFetchSize");
    }

    /* L0 */ public int getFetchSize() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getFetchSize");
        checkClosed();
        loggerExternal.exiting(getClassNameLogging(), "getFloat", fetchSize);
        return fetchSize;
    }

    /* L0 */ public int getType() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getType");
        checkClosed();

        int value = stmt.getResultSetType();
        loggerExternal.exiting(getClassNameLogging(), "getType", value);
        return value;
    }

    /* L0 */ public int getConcurrency() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getConcurrency");
        checkClosed();
        int value = stmt.getResultSetConcurrency();
        loggerExternal.exiting(getClassNameLogging(), "getConcurrency", value);
        return value;

    }

    /* ---------------------- Column gets --------------------------------- */

    /**
     * Does all the common stuff necessary when calling a getter for the column at index.
     *
     * @param index
     *            the index of the column to get
     */
    Column getterGetColumn(int index) throws SQLServerException {
        // Note that we don't verify here that we're not on the insert row. According to
        // our RS cursors spec:
        // Columns on the insert row are initially in an uninitialized state. Calls to
        // update set the column state to initialized. A call to get for an
        // uninitialized column throws an exception.
        // It doesn't say anything about calls to initialized columns (i.e. what happens
        // if update has been called). Shipped behavior is that the value returned
        // is the value of the current row, not the updated value in the insert row!

        verifyResultSetHasCurrentRow();
        verifyCurrentRowIsNotDeleted("R_cantGetColumnValueFromDeletedRow");
        verifyValidColumnIndex(index);

        if (updatedCurrentRow) {
            doRefreshRow();
            verifyResultSetHasCurrentRow();
        }

        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + " Getting Column:" + index);

        return loadColumn(index);
    }

    private Object getValue(int columnIndex,
            JDBCType jdbcType) throws SQLServerException {
        return getValue(columnIndex, jdbcType, null, null);
    }

    private Object getValue(int columnIndex,
            JDBCType jdbcType,
            Calendar cal) throws SQLServerException {
        return getValue(columnIndex, jdbcType, null, cal);
    }

    private Object getValue(int columnIndex,
            JDBCType jdbcType,
            InputStreamGetterArgs getterArgs) throws SQLServerException {
        return getValue(columnIndex, jdbcType, getterArgs, null);
    }

    private Object getValue(int columnIndex,
            JDBCType jdbcType,
            InputStreamGetterArgs getterArgs,
            Calendar cal) throws SQLServerException {
        Object o = getterGetColumn(columnIndex).getValue(jdbcType, getterArgs, cal, tdsReader);
        lastValueWasNull = (null == o);
        return o;
    }

    private Object getStream(int columnIndex,
            StreamType streamType) throws SQLServerException {
        Object value = getValue(columnIndex, streamType.getJDBCType(),
                new InputStreamGetterArgs(streamType, stmt.getExecProps().isResponseBufferingAdaptive(), isForwardOnly(), toString()));

        activeStream = (Closeable) value;
        return value;
    }

    private SQLXML getSQLXMLInternal(int columnIndex) throws SQLServerException {
        SQLServerSQLXML value = (SQLServerSQLXML) getValue(columnIndex, JDBCType.SQLXML,
                new InputStreamGetterArgs(StreamType.SQLXML, stmt.getExecProps().isResponseBufferingAdaptive(), isForwardOnly(), toString()));

        if (null != value)
            activeStream = value.getStream();
        return value;
    }

    public java.io.InputStream getAsciiStream(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getAsciiStream", columnIndex);
        checkClosed();
        InputStream value = (InputStream) getStream(columnIndex, StreamType.ASCII);
        loggerExternal.exiting(getClassNameLogging(), "getAsciiStream", value);
        return value;
    }

    public java.io.InputStream getAsciiStream(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getAsciiStream", columnName);
        checkClosed();
        InputStream value = (InputStream) getStream(findColumn(columnName), StreamType.ASCII);
        loggerExternal.exiting(getClassNameLogging(), "getAsciiStream", value);
        return value;
    }

    @Deprecated
    public BigDecimal getBigDecimal(int columnIndex,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getBigDecimal", new Object[] {columnIndex, scale});
        checkClosed();
        BigDecimal value = (BigDecimal) getValue(columnIndex, JDBCType.DECIMAL);
        if (null != value)
            value = value.setScale(scale, BigDecimal.ROUND_DOWN);
        loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value);
        return value;
    }

    @Deprecated
    public BigDecimal getBigDecimal(String columnName,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "columnName", new Object[] {columnName, scale});
        checkClosed();
        BigDecimal value = (BigDecimal) getValue(findColumn(columnName), JDBCType.DECIMAL);
        if (null != value)
            value = value.setScale(scale, BigDecimal.ROUND_DOWN);
        loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value);
        return value;
    }

    public java.io.InputStream getBinaryStream(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBinaryStream", columnIndex);
        checkClosed();
        InputStream value = (InputStream) getStream(columnIndex, StreamType.BINARY);
        loggerExternal.exiting(getClassNameLogging(), "getBinaryStream", value);
        return value;
    }

    public java.io.InputStream getBinaryStream(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBinaryStream", columnName);
        checkClosed();
        InputStream value = (InputStream) getStream(findColumn(columnName), StreamType.BINARY);
        loggerExternal.exiting(getClassNameLogging(), "getBinaryStream", value);
        return value;
    }

    public boolean getBoolean(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBoolean", columnIndex);
        checkClosed();
        Boolean value = (Boolean) getValue(columnIndex, JDBCType.BIT);
        loggerExternal.exiting(getClassNameLogging(), "getBoolean", value);
        return null != value ? value.booleanValue() : false;
    }

    public boolean getBoolean(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBoolean", columnName);
        checkClosed();
        Boolean value = (Boolean) getValue(findColumn(columnName), JDBCType.BIT);
        loggerExternal.exiting(getClassNameLogging(), "getBoolean", value);
        return null != value ? value.booleanValue() : false;
    }

    public byte getByte(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getByte", columnIndex);
        checkClosed();
        Short value = (Short) getValue(columnIndex, JDBCType.TINYINT);
        loggerExternal.exiting(getClassNameLogging(), "getByte", value);
        return null != value ? value.byteValue() : 0;
    }

    public byte getByte(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getByte", columnName);
        checkClosed();
        Short value = (Short) getValue(findColumn(columnName), JDBCType.TINYINT);
        loggerExternal.exiting(getClassNameLogging(), "getByte", value);
        return null != value ? value.byteValue() : 0;
    }

    public byte[] getBytes(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBytes", columnIndex);
        checkClosed();
        byte[] value = (byte[]) getValue(columnIndex, JDBCType.BINARY);
        loggerExternal.exiting(getClassNameLogging(), "getBytes", value);
        return value;
    }

    public byte[] getBytes(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBytes", columnName);
        checkClosed();
        byte[] value = (byte[]) getValue(findColumn(columnName), JDBCType.BINARY);
        loggerExternal.exiting(getClassNameLogging(), "getBytes", value);
        return value;
    }

    public java.sql.Date getDate(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getDate", columnIndex);
        checkClosed();
        java.sql.Date value = (java.sql.Date) getValue(columnIndex, JDBCType.DATE);
        loggerExternal.exiting(getClassNameLogging(), "getDate", value);
        return value;
    }

    public java.sql.Date getDate(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getDate", columnName);
        checkClosed();
        java.sql.Date value = (java.sql.Date) getValue(findColumn(columnName), JDBCType.DATE);
        loggerExternal.exiting(getClassNameLogging(), "getDate", value);
        return value;
    }

    public java.sql.Date getDate(int columnIndex,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getDate", new Object[] {columnIndex, cal});
        checkClosed();
        java.sql.Date value = (java.sql.Date) getValue(columnIndex, JDBCType.DATE, cal);
        loggerExternal.exiting(getClassNameLogging(), "getDate", value);
        return value;
    }

    public java.sql.Date getDate(String colName,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getDate", new Object[] {colName, cal});
        checkClosed();
        java.sql.Date value = (java.sql.Date) getValue(findColumn(colName), JDBCType.DATE, cal);
        loggerExternal.exiting(getClassNameLogging(), "getDate", value);
        return value;
    }

    public double getDouble(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getDouble", columnIndex);
        checkClosed();
        Double value = (Double) getValue(columnIndex, JDBCType.DOUBLE);
        loggerExternal.exiting(getClassNameLogging(), "getDouble", value);
        return null != value ? value.doubleValue() : 0;
    }

    public double getDouble(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getDouble", columnName);
        checkClosed();
        Double value = (Double) getValue(findColumn(columnName), JDBCType.DOUBLE);
        loggerExternal.exiting(getClassNameLogging(), "getDouble", value);
        return null != value ? value.doubleValue() : 0;
    }

    public float getFloat(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getFloat", columnIndex);
        checkClosed();
        Float value = (Float) getValue(columnIndex, JDBCType.REAL);
        loggerExternal.exiting(getClassNameLogging(), "getFloat", value);
        return null != value ? value.floatValue() : 0;
    }

    public float getFloat(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getFloat", columnName);
        checkClosed();
        Float value = (Float) getValue(findColumn(columnName), JDBCType.REAL);
        loggerExternal.exiting(getClassNameLogging(), "getFloat", value);
        return null != value ? value.floatValue() : 0;
    }

    public int getInt(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getInt", columnIndex);
        checkClosed();
        Integer value = (Integer) getValue(columnIndex, JDBCType.INTEGER);
        loggerExternal.exiting(getClassNameLogging(), "getInt", value);
        return null != value ? value.intValue() : 0;
    }

    public int getInt(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getInt", columnName);
        checkClosed();
        Integer value = (Integer) getValue(findColumn(columnName), JDBCType.INTEGER);
        loggerExternal.exiting(getClassNameLogging(), "getInt", value);
        return null != value ? value.intValue() : 0;
    }

    public long getLong(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getLong", columnIndex);
        checkClosed();
        Long value = (Long) getValue(columnIndex, JDBCType.BIGINT);
        loggerExternal.exiting(getClassNameLogging(), "getLong", value);
        return null != value ? value.longValue() : 0;
    }

    public long getLong(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getLong", columnName);
        checkClosed();
        Long value = (Long) getValue(findColumn(columnName), JDBCType.BIGINT);
        loggerExternal.exiting(getClassNameLogging(), "getLong", value);
        return null != value ? value.longValue() : 0;
    }

    public java.sql.ResultSetMetaData getMetaData() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getMetaData");
        checkClosed();
        if (metaData == null)
            metaData = new SQLServerResultSetMetaData(stmt.connection, this);
        loggerExternal.exiting(getClassNameLogging(), "getMetaData", metaData);
        return metaData;
    }

    public Object getObject(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getObject", columnIndex);
        checkClosed();
        Object value = getValue(columnIndex, getterGetColumn(columnIndex).getTypeInfo().getSSType().getJDBCType());
        loggerExternal.exiting(getClassNameLogging(), "getObject", value);
        return value;
    }

    public  T getObject(int columnIndex,
            Class type) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC41();

        // The driver currently does not implement the optional JDBC APIs
        throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported"));
    }

    public Object getObject(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getObject", columnName);
        checkClosed();
        Object value = getObject(findColumn(columnName));
        loggerExternal.exiting(getClassNameLogging(), "getObject", value);
        return value;
    }

    public  T getObject(String columnName,
            Class type) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC41();

        // The driver currently does not implement the optional JDBC APIs
        throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported"));
    }

    public short getShort(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getShort", columnIndex);
        checkClosed();
        Short value = (Short) getValue(columnIndex, JDBCType.SMALLINT);
        loggerExternal.exiting(getClassNameLogging(), "getShort", value);
        return null != value ? value.shortValue() : 0;
    }

    public short getShort(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getShort", columnName);
        checkClosed();
        Short value = (Short) getValue(findColumn(columnName), JDBCType.SMALLINT);
        loggerExternal.exiting(getClassNameLogging(), "getShort", value);
        return null != value ? value.shortValue() : 0;
    }

    public String getString(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getString", columnIndex);
        checkClosed();

        String value = null;
        Object objectValue = getValue(columnIndex, JDBCType.CHAR);
        if (null != objectValue) {
            value = objectValue.toString();
        }
        loggerExternal.exiting(getClassNameLogging(), "getString", value);
        return value;
    }

    public String getString(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getString", columnName);
        checkClosed();

        String value = null;
        Object objectValue = getValue(findColumn(columnName), JDBCType.CHAR);
        if (null != objectValue) {
            value = objectValue.toString();
        }
        loggerExternal.exiting(getClassNameLogging(), "getString", value);
        return value;
    }

    public String getNString(int columnIndex) throws SQLException {
        loggerExternal.entering(getClassNameLogging(), "getNString", columnIndex);
        DriverJDBCVersion.checkSupportsJDBC4();
        checkClosed();
        String value = (String) getValue(columnIndex, JDBCType.NCHAR);
        loggerExternal.exiting(getClassNameLogging(), "getNString", value);
        return value;
    }

    public String getNString(String columnLabel) throws SQLException {
        loggerExternal.entering(getClassNameLogging(), "getNString", columnLabel);
        DriverJDBCVersion.checkSupportsJDBC4();
        checkClosed();
        String value = (String) getValue(findColumn(columnLabel), JDBCType.NCHAR);
        loggerExternal.exiting(getClassNameLogging(), "getNString", value);
        return value;
    }

    /**
     * Retrieves the value of the designated column in the current row of this ResultSet object as a microsoft.sql.datetimeoffset object in the Java
     * programming language.
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2, ...
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLException
     *             when an error occurs
     */
    public String getUniqueIdentifier(int columnIndex) throws SQLException {
        loggerExternal.entering(getClassNameLogging(), "getUniqueIdentifier", columnIndex);
        checkClosed();
        String value = (String) getValue(columnIndex, JDBCType.GUID);
        loggerExternal.exiting(getClassNameLogging(), "getUniqueIdentifier", value);
        return value;
    }

    /**
     * Retrieves the value of the designated column in the current row of this ResultSet object as a microsoft.sql.datetimeoffset object in the Java
     * programming language.
     * 
     * @param columnLabel
     *            the name of the column
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLException
     *             when an error occurs
     */
    public String getUniqueIdentifier(String columnLabel) throws SQLException {
        loggerExternal.entering(getClassNameLogging(), "getUniqueIdentifier", columnLabel);
        checkClosed();
        String value = (String) getValue(findColumn(columnLabel), JDBCType.GUID);
        loggerExternal.exiting(getClassNameLogging(), "getUniqueIdentifier", value);
        return value;
    }

    public java.sql.Time getTime(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getTime", columnIndex);
        checkClosed();
        java.sql.Time value = (java.sql.Time) getValue(columnIndex, JDBCType.TIME);
        loggerExternal.exiting(getClassNameLogging(), "getTime", value);
        return value;
    }

    public java.sql.Time getTime(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getTime", columnName);
        checkClosed();
        java.sql.Time value = (java.sql.Time) getValue(findColumn(columnName), JDBCType.TIME);
        loggerExternal.exiting(getClassNameLogging(), "getTime", value);
        return value;
    }

    public java.sql.Time getTime(int columnIndex,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getTime", new Object[] {columnIndex, cal});
        checkClosed();
        java.sql.Time value = (java.sql.Time) getValue(columnIndex, JDBCType.TIME, cal);
        loggerExternal.exiting(getClassNameLogging(), "getTime", value);
        return value;
    }

    public java.sql.Time getTime(String colName,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getTime", new Object[] {colName, cal});
        checkClosed();
        java.sql.Time value = (java.sql.Time) getValue(findColumn(colName), JDBCType.TIME, cal);
        loggerExternal.exiting(getClassNameLogging(), "getTime", value);
        return value;
    }

    public java.sql.Timestamp getTimestamp(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getTimestamp", columnIndex);
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(columnIndex, JDBCType.TIMESTAMP);
        loggerExternal.exiting(getClassNameLogging(), "getTimestamp", value);
        return value;
    }

    public java.sql.Timestamp getTimestamp(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getTimestamp", columnName);
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(columnName), JDBCType.TIMESTAMP);
        loggerExternal.exiting(getClassNameLogging(), "getTimestamp", value);
        return value;
    }

    public java.sql.Timestamp getTimestamp(int columnIndex,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getTimestamp", new Object[] {columnIndex, cal});
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(columnIndex, JDBCType.TIMESTAMP, cal);
        loggerExternal.exiting(getClassNameLogging(), "getTimeStamp", value);
        return value;
    }

    public java.sql.Timestamp getTimestamp(String colName,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getTimestamp", new Object[] {colName, cal});
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(colName), JDBCType.TIMESTAMP, cal);
        loggerExternal.exiting(getClassNameLogging(), "getTimestamp", value);
        return value;
    }

    /**
     * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming
     * language.
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2, ...
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             when an error occurs
     */
    public java.sql.Timestamp getDateTime(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getDateTime", columnIndex);
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(columnIndex, JDBCType.TIMESTAMP);
        loggerExternal.exiting(getClassNameLogging(), "getDateTime", value);
        return value;
    }

    /**
     * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming
     * language.
     * 
     * @param columnName
     *            is the name of the column
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             If any errors occur.
     */
    public java.sql.Timestamp getDateTime(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getDateTime", columnName);
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(columnName), JDBCType.TIMESTAMP);
        loggerExternal.exiting(getClassNameLogging(), "getDateTime", value);
        return value;
    }

    /**
     * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming
     * language. This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does
     * not store timezone information.
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2, ...
     * @param cal
     *            the java.util.Calendar object to use in constructing the dateTime
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             If any errors occur.
     */
    public java.sql.Timestamp getDateTime(int columnIndex,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getDateTime", new Object[] {columnIndex, cal});
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(columnIndex, JDBCType.TIMESTAMP, cal);
        loggerExternal.exiting(getClassNameLogging(), "getDateTime", value);
        return value;
    }

    /**
     * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming
     * language. This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does
     * not store timezone information.
     * 
     * @param colName
     *            the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the
     *            column
     * @param cal
     *            the java.util.Calendar object to use in constructing the dateTime
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             If any errors occur.
     */
    public java.sql.Timestamp getDateTime(String colName,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getDateTime", new Object[] {colName, cal});
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(colName), JDBCType.TIMESTAMP, cal);
        loggerExternal.exiting(getClassNameLogging(), "getDateTime", value);
        return value;
    }

    /**
     * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming
     * language.
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2, ...
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             when an error occurs
     */
    public java.sql.Timestamp getSmallDateTime(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", columnIndex);
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(columnIndex, JDBCType.TIMESTAMP);
        loggerExternal.exiting(getClassNameLogging(), "getSmallDateTime", value);
        return value;
    }

    /**
     * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming
     * language.
     * 
     * @param columnName
     *            is the name of a column.
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             If any errors occur.
     */
    public java.sql.Timestamp getSmallDateTime(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", columnName);
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(columnName), JDBCType.TIMESTAMP);
        loggerExternal.exiting(getClassNameLogging(), "getSmallDateTime", value);
        return value;
    }

    /**
     * Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming
     * language.
     * 
     * @param columnIndex
     *            the first column is 1, the second is 2, ...
     * @param cal
     *            the java.util.Calendar object to use in constructing the smalldateTime
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             If any errors occur.
     */
    public java.sql.Timestamp getSmallDateTime(int columnIndex,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", new Object[] {columnIndex, cal});
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(columnIndex, JDBCType.TIMESTAMP, cal);
        loggerExternal.exiting(getClassNameLogging(), "getSmallDateTime", value);
        return value;
    }

    /**
     * 
     * @param colName
     *            The name of a column
     * @param cal
     *            the java.util.Calendar object to use in constructing the smalldateTime
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             If any errors occur.
     */
    public java.sql.Timestamp getSmallDateTime(String colName,
            Calendar cal) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", new Object[] {colName, cal});
        checkClosed();
        java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(colName), JDBCType.TIMESTAMP, cal);
        loggerExternal.exiting(getClassNameLogging(), "getSmallDateTime", value);
        return value;
    }

    public microsoft.sql.DateTimeOffset getDateTimeOffset(int columnIndex) throws SQLException {
        loggerExternal.entering(getClassNameLogging(), "getDateTimeOffset", columnIndex);
        checkClosed();

        // DateTimeOffset is not supported with SQL Server versions earlier than Katmai
        if (!stmt.connection.isKatmaiOrLater())
            throw new SQLServerException(SQLServerException.getErrString("R_notSupported"), SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET,
                    null);

        microsoft.sql.DateTimeOffset value = (microsoft.sql.DateTimeOffset) getValue(columnIndex, JDBCType.DATETIMEOFFSET);
        loggerExternal.exiting(getClassNameLogging(), "getDateTimeOffset", value);
        return value;
    }

    public microsoft.sql.DateTimeOffset getDateTimeOffset(String columnName) throws SQLException {
        loggerExternal.entering(getClassNameLogging(), "getDateTimeOffset", columnName);
        checkClosed();

        // DateTimeOffset is not supported with SQL Server versions earlier than Katmai
        if (!stmt.connection.isKatmaiOrLater())
            throw new SQLServerException(SQLServerException.getErrString("R_notSupported"), SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET,
                    null);

        microsoft.sql.DateTimeOffset value = (microsoft.sql.DateTimeOffset) getValue(findColumn(columnName), JDBCType.DATETIMEOFFSET);
        loggerExternal.exiting(getClassNameLogging(), "getDateTimeOffset", value);
        return value;
    }

    @Deprecated
    public java.io.InputStream getUnicodeStream(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getUnicodeStream", columnIndex);
        NotImplemented();
        return null;
    }

    @Deprecated
    public java.io.InputStream getUnicodeStream(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getUnicodeStream", columnName);
        NotImplemented();
        return null;
    }

    public Object getObject(int i,
            java.util.Map> map) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "getObject", new Object[] {i, map});
        NotImplemented();
        return null;
    }

    public Ref getRef(int i) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getRef");
        NotImplemented();
        return null;
    }

    public Blob getBlob(int i) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBlob", i);
        checkClosed();
        Blob value = (Blob) getValue(i, JDBCType.BLOB);
        loggerExternal.exiting(getClassNameLogging(), "getBlob", value);
        return value;
    }

    public Blob getBlob(String colName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBlob", colName);
        checkClosed();
        Blob value = (Blob) getValue(findColumn(colName), JDBCType.BLOB);
        loggerExternal.exiting(getClassNameLogging(), "getBlob", value);
        return value;
    }

    public Clob getClob(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getClob", columnIndex);
        checkClosed();
        Clob value = (Clob) getValue(columnIndex, JDBCType.CLOB);
        loggerExternal.exiting(getClassNameLogging(), "getClob", value);
        return value;
    }

    public Clob getClob(String colName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getClob", colName);
        checkClosed();
        Clob value = (Clob) getValue(findColumn(colName), JDBCType.CLOB);
        loggerExternal.exiting(getClassNameLogging(), "getClob", value);
        return value;
    }

    public NClob getNClob(int columnIndex) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        loggerExternal.entering(getClassNameLogging(), "getNClob", columnIndex);
        checkClosed();
        NClob value = (NClob) getValue(columnIndex, JDBCType.NCLOB);
        loggerExternal.exiting(getClassNameLogging(), "getNClob", value);
        return value;
    }

    public NClob getNClob(String columnLabel) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        loggerExternal.entering(getClassNameLogging(), "getNClob", columnLabel);
        checkClosed();
        NClob value = (NClob) getValue(findColumn(columnLabel), JDBCType.NCLOB);
        loggerExternal.exiting(getClassNameLogging(), "getNClob", value);
        return value;
    }

    public Array getArray(int i) throws SQLServerException {
        NotImplemented();
        return null;
    }

    public Object getObject(String colName,
            java.util.Map> map) throws SQLServerException {
        NotImplemented();
        return null;
    }

    public Ref getRef(String colName) throws SQLServerException {
        NotImplemented();
        return null;
    }

    public Array getArray(String colName) throws SQLServerException {
        NotImplemented();
        return null;
    }

    public String getCursorName() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getCursorName");
        SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_positionedUpdatesNotSupported"), null, false);
        loggerExternal.exiting(getClassNameLogging(), "getCursorName", null);
        return null;
    }

    public java.io.Reader getCharacterStream(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getCharacterStream", columnIndex);
        checkClosed();
        Reader value = (Reader) getStream(columnIndex, StreamType.CHARACTER);
        loggerExternal.exiting(getClassNameLogging(), "getCharacterStream", value);
        return value;
    }

    public java.io.Reader getCharacterStream(String columnName) throws SQLServerException {
        checkClosed();
        loggerExternal.entering(getClassNameLogging(), "getCharacterStream", columnName);
        Reader value = (Reader) getStream(findColumn(columnName), StreamType.CHARACTER);
        loggerExternal.exiting(getClassNameLogging(), "getCharacterStream", value);
        return value;
    }

    public Reader getNCharacterStream(int columnIndex) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", columnIndex);
        checkClosed();
        Reader value = (Reader) getStream(columnIndex, StreamType.NCHARACTER);
        loggerExternal.exiting(getClassNameLogging(), "getNCharacterStream", value);
        return value;
    }

    public Reader getNCharacterStream(String columnLabel) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", columnLabel);
        checkClosed();
        Reader value = (Reader) getStream(findColumn(columnLabel), StreamType.NCHARACTER);
        loggerExternal.exiting(getClassNameLogging(), "getNCharacterStream", value);
        return value;
    }

    public BigDecimal getBigDecimal(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBigDecimal", columnIndex);
        checkClosed();
        BigDecimal value = (BigDecimal) getValue(columnIndex, JDBCType.DECIMAL);
        loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value);
        return value;
    }

    public BigDecimal getBigDecimal(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getBigDecimal", columnName);
        checkClosed();
        BigDecimal value = (BigDecimal) getValue(findColumn(columnName), JDBCType.DECIMAL);
        loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value);
        return value;
    }

    /**
     * Retrieves the value of the column specified as a java.math.BigDecimal object.
     * 
     * @param columnIndex
     *            The zero-based ordinal of a column.
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             when an error occurs
     */
    public BigDecimal getMoney(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getMoney", columnIndex);
        checkClosed();
        BigDecimal value = (BigDecimal) getValue(columnIndex, JDBCType.DECIMAL);
        loggerExternal.exiting(getClassNameLogging(), "getMoney", value);
        return value;
    }

    /**
     * Retrieves the value of the column specified as a java.math.BigDecimal object.
     * 
     * @param columnName
     *            is the name of a column.
     * @return the column value; if the value is SQL NULL, the value returned is null.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public BigDecimal getMoney(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getMoney", columnName);
        checkClosed();
        BigDecimal value = (BigDecimal) getValue(findColumn(columnName), JDBCType.DECIMAL);
        loggerExternal.exiting(getClassNameLogging(), "getMoney", value);
        return value;
    }

    /**
     * Retrieves the value of the column specified as a java.math.BigDecimal object.
     * 
     * @param columnIndex
     *            The zero-based ordinal of a column.
     * @return the column value; if the value is SQL NULL, the value returned is null
     * @throws SQLServerException
     *             If any errors occur.
     */
    public BigDecimal getSmallMoney(int columnIndex) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getSmallMoney", columnIndex);
        checkClosed();
        BigDecimal value = (BigDecimal) getValue(columnIndex, JDBCType.DECIMAL);
        loggerExternal.exiting(getClassNameLogging(), "getSmallMoney", value);
        return value;
    }

    /**
     * Retrieves the value of the column specified as a java.math.BigDecimal object.
     * 
     * @param columnName
     *            is the name of a column.
     * @return the column value; if the value is SQL NULL, the value returned is null.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public BigDecimal getSmallMoney(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getSmallMoney", columnName);
        checkClosed();
        BigDecimal value = (BigDecimal) getValue(findColumn(columnName), JDBCType.DECIMAL);
        loggerExternal.exiting(getClassNameLogging(), "getSmallMoney", value);
        return value;
    }

    public RowId getRowId(int columnIndex) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();

        // Not implemented
        throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported"));
    }

    public RowId getRowId(String columnLabel) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();

        // Not implemented
        throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported"));
    }

    public SQLXML getSQLXML(int columnIndex) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        loggerExternal.entering(getClassNameLogging(), "getSQLXML", columnIndex);
        SQLXML xml = getSQLXMLInternal(columnIndex);
        loggerExternal.exiting(getClassNameLogging(), "getSQLXML", xml);
        return xml;
    }

    public SQLXML getSQLXML(String columnLabel) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        loggerExternal.entering(getClassNameLogging(), "getSQLXML", columnLabel);
        SQLXML xml = getSQLXMLInternal(findColumn(columnLabel));
        loggerExternal.exiting(getClassNameLogging(), "getSQLXML", xml);
        return xml;
    }

    public boolean rowUpdated() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "rowUpdated");
        checkClosed();
        // From JDBC spec:
        // Throws SQLException if the concurrency of this ResultSet object is CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();

        // From ResultSet cursor feature spec:
        // SQL Server does not detect updated rows for any cursor type
        loggerExternal.exiting(getClassNameLogging(), "rowUpdated", false);
        return false;
    }

    public boolean rowInserted() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "rowInserted");
        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the concurrency of this ResultSet object is CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();

        // From ResultSet cursor feature spec:
        // SQL Server does not detect inserted rows for any cursor type
        loggerExternal.exiting(getClassNameLogging(), "rowInserted", false);
        return false;
    }

    public boolean rowDeleted() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "rowDeleted");
        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the concurrency of this ResultSet object is CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();

        if (isOnInsertRow || !hasCurrentRow())
            return false;

        boolean deleted = currentRowDeleted();
        loggerExternal.exiting(getClassNameLogging(), "rowDeleted", deleted);
        return deleted;
    }

    /**
     * Determines whether the current row of this result set is deleted.
     *
     * A row may be deleted via the result set cursor (via ResultSet.deleteRow) or it may have been deleted outside the cursor. This function checks
     * for both possibilities.
     */
    private boolean currentRowDeleted() throws SQLServerException {
        // Never call this function without a current row
        assert hasCurrentRow();

        // Having a current row implies we have a fetch buffer in which that row exists.
        assert null != tdsReader;

        return deletedCurrentRow || (0 != serverCursorId && TDS.ROWSTAT_FETCH_MISSING == loadColumn(columns.length).getInt(tdsReader));
    }

    /* ---------------- Column updates ---------------------- */

    /**
     * Does all the common stuff necessary when calling a getter for the column at index.
     *
     * @param index
     *            the index of the column to get
     */
    private Column updaterGetColumn(int index) throws SQLServerException {
        // From JDBC spec:
        // Throws SQLException if the concurrency of this ResultSet object is CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();

        verifyValidColumnIndex(index);

        // Verify that the column is updatable (i.e. that it is not a computed column).
        if (!columns[index - 1].isUpdatable()) {
            SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_cantUpdateColumn"), "07009", false);
        }

        // Column values on the insert row are always updatable,
        // regardless whether this ResultSet has any current row.
        if (!isOnInsertRow) {
            // Column values can only be updated on the insert row and the current row.
            // We just determined that we're not on the insert row, so make sure
            // that this ResultSet has a current row (i.e. that the ResultSet's position
            // is not before the first row or after the last row).
            if (!hasCurrentRow()) {
                SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_resultsetNoCurrentRow"), null, true);
            }

            // A current row exists. Its column values are updatable only if the row
            // is not a deleted row ("hole").
            verifyCurrentRowIsNotDeleted("R_cantUpdateDeletedRow");
        }

        return getColumn(index);
    }

    private void updateValue(int columnIndex,
            JDBCType jdbcType,
            Object value,
            JavaType javaType,
            boolean forceEncrypt) throws SQLServerException {
        updaterGetColumn(columnIndex).updateValue(jdbcType, value, javaType, null, null, null, stmt.connection, stmt.stmtColumnEncriptionSetting,
                null, forceEncrypt, columnIndex);
    }

    private void updateValue(int columnIndex,
            JDBCType jdbcType,
            Object value,
            JavaType javaType,
            Calendar cal,
            boolean forceEncrypt) throws SQLServerException {
        updaterGetColumn(columnIndex).updateValue(jdbcType, value, javaType, null, cal, null, stmt.connection, stmt.stmtColumnEncriptionSetting, null,
                forceEncrypt, columnIndex);
    }

    private void updateValue(int columnIndex,
            JDBCType jdbcType,
            Object value,
            JavaType javaType,
            Integer precision,
            Integer scale,
            boolean forceEncrypt) throws SQLServerException {
        updaterGetColumn(columnIndex).updateValue(jdbcType, value, javaType, null, null, scale, stmt.connection, stmt.stmtColumnEncriptionSetting,
                precision, forceEncrypt, columnIndex);
    }

    private void updateStream(int columnIndex,
            StreamType streamType,
            Object value,
            JavaType javaType,
            long length) throws SQLServerException {
        updaterGetColumn(columnIndex).updateValue(streamType.getJDBCType(), value, javaType, new StreamSetterArgs(streamType, length), null, null,
                stmt.connection, stmt.stmtColumnEncriptionSetting, null, false, columnIndex);
    }

    private void updateSQLXMLInternal(int columnIndex,
            SQLXML value) throws SQLServerException {
        updaterGetColumn(columnIndex).updateValue(JDBCType.SQLXML, value, JavaType.SQLXML,
                new StreamSetterArgs(StreamType.SQLXML, DataTypes.UNKNOWN_STREAM_LENGTH), null, null, stmt.connection,
                stmt.stmtColumnEncriptionSetting, null, false, columnIndex);
    }

    public void updateNull(int index) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "updateNull", index);

        checkClosed();
        updateValue(index, updaterGetColumn(index).getTypeInfo().getSSType().getJDBCType(), null, JavaType.OBJECT, false);

        loggerExternal.exiting(getClassNameLogging(), "updateNull");
    }

    public void updateBoolean(int index,
            boolean x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {index, x});
        checkClosed();
        updateValue(index, JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBoolean");
    }

    /**
     * Updates the designated column with a boolean value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateBoolean(int index,
            boolean x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {index, x, forceEncrypt});
        checkClosed();
        updateValue(index, JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateBoolean");
    }

    public void updateByte(int index,
            byte x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateByte", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.TINYINT, Byte.valueOf(x), JavaType.BYTE, false);

        loggerExternal.exiting(getClassNameLogging(), "updateByte");
    }

    /**
     * Updates the designated column with a byte value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateByte(int index,
            byte x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateByte", new Object[] {index, x, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.TINYINT, Byte.valueOf(x), JavaType.BYTE, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateByte");
    }

    public void updateShort(int index,
            short x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, false);

        loggerExternal.exiting(getClassNameLogging(), "updateShort");
    }

    /**
     * Updates the designated column with a short value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateShort(int index,
            short x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {index, x, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateShort");
    }

    public void updateInt(int index,
            int x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.INTEGER, Integer.valueOf(x), JavaType.INTEGER, false);

        loggerExternal.exiting(getClassNameLogging(), "updateInt");
    }

    /**
     * Updates the designated column with an int value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateInt(int index,
            int x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {index, x, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.INTEGER, Integer.valueOf(x), JavaType.INTEGER, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateInt");
    }

    public void updateLong(int index,
            long x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, false);

        loggerExternal.exiting(getClassNameLogging(), "updateLong");
    }

    /**
     * Updates the designated column with a long value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateLong(int index,
            long x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {index, x, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateLong");
    }

    public void updateFloat(int index,
            float x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, false);

        loggerExternal.exiting(getClassNameLogging(), "updateFloat");
    }

    /**
     * Updates the designated column with a float value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateFloat(int index,
            float x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {index, x, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateFloat");
    }

    public void updateDouble(int index,
            double x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDouble");
    }

    /**
     * Updates the designated column with a double value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateDouble(int index,
            double x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {index, x, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateDouble");
    }

    /**
     * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateMoney(int index,
            BigDecimal x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateMoney", new Object[] {index, x});
        checkClosed();
        updateValue(index, JDBCType.MONEY, x, JavaType.BIGDECIMAL, false);

        loggerExternal.exiting(getClassNameLogging(), "updateMoney");
    }

    /**
     * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateMoney(int index,
            BigDecimal x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateMoney", new Object[] {index, x, forceEncrypt});
        checkClosed();
        updateValue(index, JDBCType.MONEY, x, JavaType.BIGDECIMAL, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateMoney");
    }

    /**
     * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnName
     *            is the column name
     * @param x
     *            the new column value
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateMoney(String columnName,
            BigDecimal x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateMoney", new Object[] {columnName, x});
        checkClosed();
        updateValue(findColumn(columnName), JDBCType.MONEY, x, JavaType.BIGDECIMAL, false);

        loggerExternal.exiting(getClassNameLogging(), "updateMoney");
    }

    /**
     * Updates the designated column with a money value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnName
     *            the column name
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateMoney(String columnName,
            BigDecimal x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateMoney", new Object[] {columnName, x, forceEncrypt});
        checkClosed();
        updateValue(findColumn(columnName), JDBCType.MONEY, x, JavaType.BIGDECIMAL, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateMoney");
    }

    /**
     * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or
     * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods
     * are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateSmallMoney(int index,
            BigDecimal x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallMoney", new Object[] {index, x});
        checkClosed();
        updateValue(index, JDBCType.SMALLMONEY, x, JavaType.BIGDECIMAL, false);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallMoney");
    }

    /**
     * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or
     * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods
     * are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateSmallMoney(int index,
            BigDecimal x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallMoney", new Object[] {index, x, forceEncrypt});
        checkClosed();
        updateValue(index, JDBCType.SMALLMONEY, x, JavaType.BIGDECIMAL, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallMoney");
    }

    /**
     * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or
     * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods
     * are called to update the database.
     *
     * @param columnName
     *            the column name
     * @param x
     *            the new column value
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateSmallMoney(String columnName,
            BigDecimal x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallMoney", new Object[] {columnName, x});
        checkClosed();
        updateValue(findColumn(columnName), JDBCType.SMALLMONEY, x, JavaType.BIGDECIMAL, false);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallMoney");
    }

    /**
     * Updates the designated column with a smallmoney value. The updater methods are used to update column values in the current row or
     * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods
     * are called to update the database.
     *
     * @param columnName
     *            the column name
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateSmallMoney(String columnName,
            BigDecimal x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallMoney", new Object[] {columnName, x, forceEncrypt});
        checkClosed();
        updateValue(findColumn(columnName), JDBCType.SMALLMONEY, x, JavaType.BIGDECIMAL, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallMoney");
    }

    public void updateBigDecimal(int index,
            BigDecimal x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBigDecimal", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal");
    }

    /**
     * Updates the designated column with a java.math.BigDecimal value. The updater methods are used to update column values in the
     * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or
     * insertRow methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param precision
     *            the precision of the column
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateBigDecimal(int index,
            BigDecimal x,
            Integer precision,
            Integer scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBigDecimal", new Object[] {index, x, scale});

        checkClosed();
        updateValue(index, JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, precision, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal");
    }

    /**
     * Updates the designated column with a java.math.BigDecimal value. The updater methods are used to update column values in the
     * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or
     * insertRow methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param precision
     *            the precision of the column
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateBigDecimal(int index,
            BigDecimal x,
            Integer precision,
            Integer scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBigDecimal", new Object[] {index, x, scale, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, precision, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal");
    }

    public void updateString(int columnIndex,
            String stringValue) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateString", new Object[] {columnIndex, stringValue});

        checkClosed();
        updateValue(columnIndex, JDBCType.VARCHAR, stringValue, JavaType.STRING, false);

        loggerExternal.exiting(getClassNameLogging(), "updateString");
    }

    /**
     * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnIndex
     *            the first column is 1, the second is 2, ...
     * @param stringValue
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateString(int columnIndex,
            String stringValue,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateString", new Object[] {columnIndex, stringValue, forceEncrypt});

        checkClosed();
        updateValue(columnIndex, JDBCType.VARCHAR, stringValue, JavaType.STRING, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateString");
    }

    public void updateNString(int columnIndex,
            String nString) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNString", new Object[] {columnIndex, nString});

        DriverJDBCVersion.checkSupportsJDBC4();
        checkClosed();
        updateValue(columnIndex, JDBCType.NVARCHAR, nString, JavaType.STRING, false);

        loggerExternal.exiting(getClassNameLogging(), "updateNString");
    }

    /**
     * Updates the designated column with a String value. It is intended for use when updating NCHAR,NVARCHAR
     * and LONGNVARCHAR columns. The updater methods are used to update column values in the current row or the insert row. The updater
     * methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the
     * database.
     *
     * @param columnIndex
     *            the first column is 1, the second 2, ...
     * @param nString
     *            the value for the column to be updated
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLException
     *             when an error occurs
     */
    public void updateNString(int columnIndex,
            String nString,
            boolean forceEncrypt) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNString", new Object[] {columnIndex, nString, forceEncrypt});

        DriverJDBCVersion.checkSupportsJDBC4();
        checkClosed();
        updateValue(columnIndex, JDBCType.NVARCHAR, nString, JavaType.STRING, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateNString");
    }

    public void updateNString(String columnLabel,
            String nString) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNString", new Object[] {columnLabel, nString});

        DriverJDBCVersion.checkSupportsJDBC4();
        checkClosed();
        updateValue(findColumn(columnLabel), JDBCType.NVARCHAR, nString, JavaType.STRING, false);

        loggerExternal.exiting(getClassNameLogging(), "updateNString");
    }

    /**
     * Updates the designated column with a String value. It is intended for use when updating NCHAR,NVARCHAR
     * and LONGNVARCHAR columns. The updater methods are used to update column values in the current row or the insert row. The updater
     * methods do not update the underlying database; instead the updateRow or insertRow methods are called to update the
     * database.
     *
     * @param columnLabel
     *            the label for the column specified with the SQL AS clause. If the SQL AS clause was not specified, then the label is the name of the
     *            column
     * @param nString
     *            the value for the column to be updated
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLException
     *             when an error occurs
     */
    public void updateNString(String columnLabel,
            String nString,
            boolean forceEncrypt) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNString", new Object[] {columnLabel, nString, forceEncrypt});

        DriverJDBCVersion.checkSupportsJDBC4();
        checkClosed();
        updateValue(findColumn(columnLabel), JDBCType.NVARCHAR, nString, JavaType.STRING, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateNString");
    }

    public void updateBytes(int index,
            byte x[]) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBytes", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.BINARY, x, JavaType.BYTEARRAY, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBytes");
    }

    /**
     * Updates the designated column with a byte array value. The updater methods are used to update column values in the current row or
     * the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods
     * are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateBytes(int index,
            byte x[],
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBytes", new Object[] {index, x, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.BINARY, x, JavaType.BYTEARRAY, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateBytes");
    }

    public void updateDate(int index,
            java.sql.Date x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDate", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.DATE, x, JavaType.DATE, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDate");
    }

    /**
     * Updates the designated column with a java.sql.Date value. The updater methods are used to update column values in the current row
     * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateDate(int index,
            java.sql.Date x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDate", new Object[] {index, x, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.DATE, x, JavaType.DATE, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateDate");
    }

    public void updateTime(int index,
            java.sql.Time x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTime", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.TIME, x, JavaType.TIME, false);

        loggerExternal.exiting(getClassNameLogging(), "updateTime");
    }

    /**
     * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row
     * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateTime(int index,
            java.sql.Time x,
            Integer scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTime", new Object[] {index, x, scale});

        checkClosed();
        updateValue(index, JDBCType.TIME, x, JavaType.TIME, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateTime");
    }

    /**
     * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row
     * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateTime(int index,
            java.sql.Time x,
            Integer scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTime", new Object[] {index, x, scale, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.TIME, x, JavaType.TIME, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateTime");
    }

    public void updateTimestamp(int index,
            java.sql.Timestamp x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTimestamp", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, false);

        loggerExternal.exiting(getClassNameLogging(), "updateTimestamp");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateTimestamp(int index,
            java.sql.Timestamp x,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTimestamp", new Object[] {index, x, scale});

        checkClosed();
        updateValue(index, JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateTimestamp");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateTimestamp(int index,
            java.sql.Timestamp x,
            int scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTimestamp", new Object[] {index, x, scale, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateTimestamp");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateDateTime(int index,
            java.sql.Timestamp x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTime", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.DATETIME, x, JavaType.TIMESTAMP, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateDateTime(int index,
            java.sql.Timestamp x,
            Integer scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTime", new Object[] {index, x, scale});

        checkClosed();
        updateValue(index, JDBCType.DATETIME, x, JavaType.TIMESTAMP, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateDateTime(int index,
            java.sql.Timestamp x,
            Integer scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTime", new Object[] {index, x, scale, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.DATETIME, x, JavaType.TIMESTAMP, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateSmallDateTime(int index,
            java.sql.Timestamp x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallDateTime", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.SMALLDATETIME, x, JavaType.TIMESTAMP, false);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateSmallDateTime(int index,
            java.sql.Timestamp x,
            Integer scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallDateTime", new Object[] {index, x, scale});

        checkClosed();
        updateValue(index, JDBCType.SMALLDATETIME, x, JavaType.TIMESTAMP, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateSmallDateTime(int index,
            java.sql.Timestamp x,
            Integer scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallDateTime", new Object[] {index, x, scale, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.SMALLDATETIME, x, JavaType.TIMESTAMP, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime");
    }

    public void updateDateTimeOffset(int index,
            microsoft.sql.DateTimeOffset x) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.DATETIMEOFFSET, x, JavaType.DATETIMEOFFSET, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset");
    }

    /**
     * Updates the value of the column specified to the DateTimeOffset Class value, given a zero-based column ordinal.
     * 
     * @param index
     *            The zero-based ordinal of a column.
     * @param x
     *            A DateTimeOffset Class object.
     * @param scale
     *            scale of the column
     * @throws SQLException
     *             when an error occurs
     */
    public void updateDateTimeOffset(int index,
            microsoft.sql.DateTimeOffset x,
            Integer scale) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {index, x, scale});

        checkClosed();
        updateValue(index, JDBCType.DATETIMEOFFSET, x, JavaType.DATETIMEOFFSET, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset");
    }

    /**
     * Updates the value of the column specified to the DateTimeOffset Class value, given a zero-based column ordinal.
     * 
     * @param index
     *            The zero-based ordinal of a column.
     * @param x
     *            A DateTimeOffset Class object.
     * @param scale
     *            scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLException
     *             when an error occurs
     */
    public void updateDateTimeOffset(int index,
            microsoft.sql.DateTimeOffset x,
            Integer scale,
            boolean forceEncrypt) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {index, x, scale, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.DATETIMEOFFSET, x, JavaType.DATETIMEOFFSET, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset");
    }

    /**
     * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     * 
     * @param index
     *            The zero-based ordinal of a column.
     * @param x
     *            the new column value
     * @throws SQLException
     *             when an error occurs
     */
    public void updateUniqueIdentifier(int index,
            String x) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateUniqueIdentifier", new Object[] {index, x});

        checkClosed();
        updateValue(index, JDBCType.GUID, x, JavaType.STRING, null, false);

        loggerExternal.exiting(getClassNameLogging(), "updateUniqueIdentifier");
    }

    /**
     * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     * 
     * @param index
     *            The zero-based ordinal of a column.
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLException
     *             when an error occurs
     */
    public void updateUniqueIdentifier(int index,
            String x,
            boolean forceEncrypt) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateUniqueIdentifier", new Object[] {index, x, forceEncrypt});

        checkClosed();
        updateValue(index, JDBCType.GUID, x, JavaType.STRING, null, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateUniqueIdentifier");
    }

    public void updateAsciiStream(int columnIndex,
            InputStream x) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateAsciiStream", new Object[] {columnIndex, x});

        checkClosed();
        updateStream(columnIndex, StreamType.ASCII, x, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream");
    }

    public void updateAsciiStream(int index,
            java.io.InputStream x,
            int length) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateAsciiStream", new Object[] {index, x, length});

        checkClosed();
        updateStream(index, StreamType.ASCII, x, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream");
    }

    public void updateAsciiStream(int columnIndex,
            InputStream x,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        loggerExternal.entering(getClassNameLogging(), "updateAsciiStream", new Object[] {columnIndex, x, length});

        checkClosed();
        updateStream(columnIndex, StreamType.ASCII, x, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream");
    }

    public void updateAsciiStream(String columnLabel,
            InputStream x) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateAsciiStream", new Object[] {columnLabel, x});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.ASCII, x, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream");
    }

    public void updateAsciiStream(java.lang.String columnName,
            java.io.InputStream x,
            int length) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateAsciiStream", new Object[] {columnName, x, length});

        checkClosed();
        updateStream(findColumn(columnName), StreamType.ASCII, x, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream");
    }

    public void updateAsciiStream(String columnName,
            InputStream streamValue,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateAsciiStream", new Object[] {columnName, streamValue, length});

        checkClosed();
        updateStream(findColumn(columnName), StreamType.ASCII, streamValue, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream");
    }

    public void updateBinaryStream(int columnIndex,
            InputStream x) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBinaryStream", new Object[] {columnIndex, x});

        checkClosed();
        updateStream(columnIndex, StreamType.BINARY, x, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream");
    }

    public void updateBinaryStream(int columnIndex,
            InputStream streamValue,
            int length) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBinaryStream", new Object[] {columnIndex, streamValue, length});

        checkClosed();
        updateStream(columnIndex, StreamType.BINARY, streamValue, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream");
    }

    public void updateBinaryStream(int columnIndex,
            InputStream x,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBinaryStream", new Object[] {columnIndex, x, length});

        checkClosed();
        updateStream(columnIndex, StreamType.BINARY, x, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream");
    }

    public void updateBinaryStream(String columnLabel,
            InputStream x) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBinaryStream", new Object[] {columnLabel, x});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.BINARY, x, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream");
    }

    public void updateBinaryStream(String columnName,
            InputStream streamValue,
            int length) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBinaryStream", new Object[] {columnName, streamValue, length});

        checkClosed();
        updateStream(findColumn(columnName), StreamType.BINARY, streamValue, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream");
    }

    public void updateBinaryStream(String columnLabel,
            InputStream x,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBinaryStream", new Object[] {columnLabel, x, length});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.BINARY, x, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateBinaryStream");
    }

    public void updateCharacterStream(int columnIndex,
            Reader x) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateCharacterStream", new Object[] {columnIndex, x});

        checkClosed();
        updateStream(columnIndex, StreamType.CHARACTER, x, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream");
    }

    public void updateCharacterStream(int columnIndex,
            Reader readerValue,
            int length) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateCharacterStream", new Object[] {columnIndex, readerValue, length});

        checkClosed();
        updateStream(columnIndex, StreamType.CHARACTER, readerValue, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream");
    }

    public void updateCharacterStream(int columnIndex,
            Reader x,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateCharacterStream", new Object[] {columnIndex, x, length});

        checkClosed();
        updateStream(columnIndex, StreamType.CHARACTER, x, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream");
    }

    public void updateCharacterStream(String columnLabel,
            Reader reader) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateCharacterStream", new Object[] {columnLabel, reader});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.CHARACTER, reader, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream");
    }

    public void updateCharacterStream(String columnName,
            Reader readerValue,
            int length) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateCharacterStream", new Object[] {columnName, readerValue, length});

        checkClosed();
        updateStream(findColumn(columnName), StreamType.CHARACTER, readerValue, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateCharacterStream");
    }

    public void updateCharacterStream(String columnLabel,
            Reader reader,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateCharacterStream", new Object[] {columnLabel, reader, length});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.CHARACTER, reader, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream");
    }

    public void updateNCharacterStream(int columnIndex,
            Reader x) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNCharacterStream", new Object[] {columnIndex, x});

        checkClosed();
        updateStream(columnIndex, StreamType.NCHARACTER, x, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream");
    }

    public void updateNCharacterStream(int columnIndex,
            Reader x,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNCharacterStream", new Object[] {columnIndex, x, length});

        checkClosed();
        updateStream(columnIndex, StreamType.NCHARACTER, x, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream");
    }

    public void updateNCharacterStream(String columnLabel,
            Reader reader) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNCharacterStream", new Object[] {columnLabel, reader});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.NCHARACTER, reader, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream");
    }

    public void updateNCharacterStream(String columnLabel,
            Reader reader,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNCharacterStream", new Object[] {columnLabel, reader, length});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.NCHARACTER, reader, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateNCharacterStream");
    }

    public void updateObject(int index,
            Object obj) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj});

        checkClosed();
        updateObject(index, obj, null, null, null, false);

        loggerExternal.exiting(getClassNameLogging(), "updateObject");
    }

    public void updateObject(int index,
            Object x,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, x, scale});

        checkClosed();
        updateObject(index, x, Integer.valueOf(scale), null, null, false);

        loggerExternal.exiting(getClassNameLogging(), "updateObject");
    }

    /**
     * Updates the designated column with an {@code Object} value.
     *
     * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying
     * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param precision
     *            the precision of the column
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateObject(int index,
            Object x,
            int precision,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, x, scale});

        checkClosed();
        updateObject(index, x, Integer.valueOf(scale), null, precision, false);

        loggerExternal.exiting(getClassNameLogging(), "updateObject");
    }

    /**
     * Updates the designated column with an {@code Object} value.
     *
     * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying
     * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database.
     *
     * @param index
     *            the first column is 1, the second is 2, ...
     * @param x
     *            the new column value
     * @param precision
     *            the precision of the column
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateObject(int index,
            Object x,
            int precision,
            int scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, x, scale, forceEncrypt});

        checkClosed();
        updateObject(index, x, Integer.valueOf(scale), null, precision, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateObject");
    }

    protected final void updateObject(int index,
            Object x,
            Integer scale,
            JDBCType jdbcType,
            Integer precision,
            boolean forceEncrypt) throws SQLServerException {
        Column column = updaterGetColumn(index);
        SSType ssType = column.getTypeInfo().getSSType();

        if (null == x) {
            if (null == jdbcType || jdbcType.isUnsupported()) {
                // JDBCType is not specified by user or is unsupported, derive from SSType
                jdbcType = ssType.getJDBCType();
            }

            column.updateValue(jdbcType, x, JavaType.OBJECT, null, // streamSetterArgs
                    null, scale, stmt.connection, stmt.stmtColumnEncriptionSetting, precision, forceEncrypt, index);
        }
        else {
            JavaType javaType = JavaType.of(x);
            JDBCType objectJdbcType = javaType.getJDBCType(ssType, ssType.getJDBCType());

            if (null == jdbcType) {
                // JDBCType is not specified by user, derive from the object's JavaType
                jdbcType = objectJdbcType;
            }
            else {
                // Check convertibility of the value to the desired JDBC type.
                if (!objectJdbcType.convertsTo(jdbcType))
                    DataTypes.throwConversionError(objectJdbcType.toString(), jdbcType.toString());
            }

            StreamSetterArgs streamSetterArgs = null;
            switch (javaType) {
                case READER:
                    streamSetterArgs = new StreamSetterArgs(StreamType.CHARACTER, DataTypes.UNKNOWN_STREAM_LENGTH);
                    break;

                case INPUTSTREAM:
                    streamSetterArgs = new StreamSetterArgs(jdbcType.isTextual() ? StreamType.CHARACTER : StreamType.BINARY,
                            DataTypes.UNKNOWN_STREAM_LENGTH);
                    break;

                case SQLXML:
                    streamSetterArgs = new StreamSetterArgs(StreamType.SQLXML, DataTypes.UNKNOWN_STREAM_LENGTH);
                    break;

                default:
                    // Do nothing
                    break;
            }

            column.updateValue(jdbcType, x, javaType, streamSetterArgs, null, scale, stmt.connection, stmt.stmtColumnEncriptionSetting, precision,
                    forceEncrypt, index);
        }
    }

    public void updateNull(String columnName) throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "updateNull", columnName);

        checkClosed();
        int columnIndex = findColumn(columnName);
        updateValue(columnIndex, updaterGetColumn(columnIndex).getTypeInfo().getSSType().getJDBCType(), null, JavaType.OBJECT, false);

        loggerExternal.exiting(getClassNameLogging(), "updateNull");
    }

    public void updateBoolean(String columnName,
            boolean x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBoolean");
    }

    /**
     * Updates the designated column with a boolean value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnName
     *            the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             when an error occurs
     */
    public void updateBoolean(String columnName,
            boolean x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.BIT, Boolean.valueOf(x), JavaType.BOOLEAN, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateBoolean");
    }

    public void updateByte(String columnName,
            byte x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateByte", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.BINARY, x, JavaType.BYTE, false);

        loggerExternal.exiting(getClassNameLogging(), "updateByte");
    }

    /**
     * Updates the designated column with a byte value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     *
     * @param columnName
     *            the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateByte(String columnName,
            byte x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateByte", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.BINARY, x, JavaType.BYTE, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateByte");
    }

    public void updateShort(String columnName,
            short x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, false);

        loggerExternal.exiting(getClassNameLogging(), "updateShort");
    }

    /**
     * Updates the designated column with a short value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnName
     *            the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateShort(String columnName,
            short x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.SMALLINT, Short.valueOf(x), JavaType.SHORT, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateShort");
    }

    public void updateInt(String columnName,
            int x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.INTEGER, Integer.valueOf(x), JavaType.INTEGER, false);

        loggerExternal.exiting(getClassNameLogging(), "updateInt");
    }

    /**
     * Updates the designated column with an int value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateInt(String columnName,
            int x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.INTEGER, Integer.valueOf(x), JavaType.INTEGER, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateInt");
    }

    public void updateLong(String columnName,
            long x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, false);

        loggerExternal.exiting(getClassNameLogging(), "updateLong");
    }

    /**
     * Updates the designated column with a long value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateLong(String columnName,
            long x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.BIGINT, Long.valueOf(x), JavaType.LONG, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateLong");
    }

    public void updateFloat(String columnName,
            float x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, false);

        loggerExternal.exiting(getClassNameLogging(), "updateFloat");
    }

    /**
     * Updates the designated column with a float  value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateFloat(String columnName,
            float x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.REAL, Float.valueOf(x), JavaType.FLOAT, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateFloat");
    }

    public void updateDouble(String columnName,
            double x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDouble");
    }

    /**
     * Updates the designated column with a double value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateDouble(String columnName,
            double x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DOUBLE, Double.valueOf(x), JavaType.DOUBLE, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateDouble");
    }

    public void updateBigDecimal(String columnName,
            BigDecimal x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBigDecimal", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal");
    }

    /**
     * Updates the designated column with a java.sql.BigDecimal value. The updater methods are used to update column values in the
     * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or
     * insertRow methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateBigDecimal(String columnName,
            BigDecimal x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBigDecimal", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal");
    }

    /**
     * Updates the designated column with a java.sql.BigDecimal value. The updater methods are used to update column values in the
     * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or
     * insertRow methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column and Always Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set
     *            to false, the driver will not force encryption on parameters.
     * @param x
     *            BigDecimal value
     * @param precision
     *            the precision of the column
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateBigDecimal(String columnName,
            BigDecimal x,
            Integer precision,
            Integer scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBigDecimal", new Object[] {columnName, x, precision, scale});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, precision, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal");
    }

    /**
     * Updates the designated column with a java.sql.BigDecimal value. The updater methods are used to update column values in the
     * current row or the insert row. The updater methods do not update the underlying database; instead the updateRow or
     * insertRow methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column and Always Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set
     *            to false, the driver will not force encryption on parameters.
     * @param x
     *            BigDecimal value
     * @param precision
     *            the precision of the column
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateBigDecimal(String columnName,
            BigDecimal x,
            Integer precision,
            Integer scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBigDecimal", new Object[] {columnName, x, precision, scale, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DECIMAL, x, JavaType.BIGDECIMAL, precision, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateBigDecimal");
    }

    public void updateString(String columnName,
            String x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateString", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.VARCHAR, x, JavaType.STRING, false);

        loggerExternal.exiting(getClassNameLogging(), "updateString");
    }

    /**
     * Updates the designated column with a String value. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateString(String columnName,
            String x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateString", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.VARCHAR, x, JavaType.STRING, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateString");
    }

    public void updateBytes(String columnName,
            byte x[]) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBytes", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.BINARY, x, JavaType.BYTEARRAY, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBytes");
    }

    /**
     * Updates the designated column with a byte array value.
     *
     * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying
     * database; instead the updateRow or insertRow methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateBytes(String columnName,
            byte x[],
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBytes", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.BINARY, x, JavaType.BYTEARRAY, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateBytes");
    }

    public void updateDate(String columnName,
            java.sql.Date x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDate", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DATE, x, JavaType.DATE, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDate");
    }

    /**
     * Updates the designated column with a java.sql.Date value. The updater methods are used to update column values in the current row
     * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateDate(String columnName,
            java.sql.Date x,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDate", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DATE, x, JavaType.DATE, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateDate");
    }

    public void updateTime(String columnName,
            java.sql.Time x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTime", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.TIME, x, JavaType.TIME, false);

        loggerExternal.exiting(getClassNameLogging(), "updateTime");
    }

    /**
     * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row
     * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateTime(String columnName,
            java.sql.Time x,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTime", new Object[] {columnName, x, scale});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.TIME, x, JavaType.TIME, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateTime");
    }

    /**
     * Updates the designated column with a java.sql.Time value. The updater methods are used to update column values in the current row
     * or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateTime(String columnName,
            java.sql.Time x,
            int scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTime", new Object[] {columnName, x, scale, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.TIME, x, JavaType.TIME, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateTime");
    }

    public void updateTimestamp(String columnName,
            java.sql.Timestamp x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTimestamp", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, false);

        loggerExternal.exiting(getClassNameLogging(), "updateTimestamp");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateTimestamp(String columnName,
            java.sql.Timestamp x,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTimestamp", new Object[] {columnName, x, scale});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateTimestamp");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateTimestamp(String columnName,
            java.sql.Timestamp x,
            int scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateTimestamp", new Object[] {columnName, x, scale, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.TIMESTAMP, x, JavaType.TIMESTAMP, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateTimestamp");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateDateTime(String columnName,
            java.sql.Timestamp x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTime", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DATETIME, x, JavaType.TIMESTAMP, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateDateTime(String columnName,
            java.sql.Timestamp x,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTime", new Object[] {columnName, x, scale});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DATETIME, x, JavaType.TIMESTAMP, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateDateTime(String columnName,
            java.sql.Timestamp x,
            int scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTime", new Object[] {columnName, x, scale, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DATETIME, x, JavaType.TIMESTAMP, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateSmallDateTime(String columnName,
            java.sql.Timestamp x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallDateTime", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.SMALLDATETIME, x, JavaType.TIMESTAMP, false);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateSmallDateTime(String columnName,
            java.sql.Timestamp x,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallDateTime", new Object[] {columnName, x, scale});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.SMALLDATETIME, x, JavaType.TIMESTAMP, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime");
    }

    /**
     * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to update column values in the current
     * row or the insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow
     * methods are called to update the database.
     *
     * @param columnName
     *            is the name of the column
     * @param x
     *            the new column value
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateSmallDateTime(String columnName,
            java.sql.Timestamp x,
            int scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSmallDateTime", new Object[] {columnName, x, scale, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.SMALLDATETIME, x, JavaType.TIMESTAMP, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateSmallDateTime");
    }

    public void updateDateTimeOffset(String columnName,
            microsoft.sql.DateTimeOffset x) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DATETIMEOFFSET, x, JavaType.DATETIMEOFFSET, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset");
    }

    /**
     * Updates the value of the column specified to the DateTimeOffset Class value, given a column name.
     * 
     * @param columnName
     *            The name of a column.
     * @param x
     *            A DateTimeOffset Class object.
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateDateTimeOffset(String columnName,
            microsoft.sql.DateTimeOffset x,
            int scale) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {columnName, x, scale});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DATETIMEOFFSET, x, JavaType.DATETIMEOFFSET, null, scale, false);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset");
    }

    /**
     * Updates the value of the column specified to the DateTimeOffset Class value, given a column name.
     * 
     * @param columnName
     *            The name of a column.
     * @param x
     *            A DateTimeOffset Class object.
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLException
     *             If any errors occur.
     */
    public void updateDateTimeOffset(String columnName,
            microsoft.sql.DateTimeOffset x,
            int scale,
            boolean forceEncrypt) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateDateTimeOffset", new Object[] {columnName, x, scale, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.DATETIMEOFFSET, x, JavaType.DATETIMEOFFSET, null, scale, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateDateTimeOffset");
    }

    /**
     * Updates the designated column with a Stringvalue. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     * 
     * @param columnName
     *            The name of a column.
     * @param x
     *            the new column value
     * @throws SQLException
     *             If any errors occur.
     */
    public void updateUniqueIdentifier(String columnName,
            String x) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateUniqueIdentifier", new Object[] {columnName, x});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.GUID, x, JavaType.STRING, null, false);

        loggerExternal.exiting(getClassNameLogging(), "updateUniqueIdentifier");
    }

    /**
     * Updates the designated column with a Stringvalue. The updater methods are used to update column values in the current row or the
     * insert row. The updater methods do not update the underlying database; instead the updateRow or insertRow methods are
     * called to update the database.
     * 
     * @param columnName
     *            The name of a column.
     * @param x
     *            the new column value
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLException
     *             If any errors occur.
     */
    public void updateUniqueIdentifier(String columnName,
            String x,
            boolean forceEncrypt) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateUniqueIdentifier", new Object[] {columnName, x, forceEncrypt});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.GUID, x, JavaType.STRING, null, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateUniqueIdentifier");
    }

    public void updateObject(String columnName,
            Object x,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, x, scale});

        checkClosed();
        updateObject(findColumn(columnName), x, Integer.valueOf(scale), null, null, false);

        loggerExternal.exiting(getClassNameLogging(), "updateObject");
    }

    /**
     * Updates the designated column with an {@code Object} value.
     *
     * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying
     * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database.
     *
     * @param columnName
     *            The name of a column.
     * @param x
     *            the new column value
     * @param precision
     *            the precision of the column
     * @param scale
     *            the scale of the column
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateObject(String columnName,
            Object x,
            int precision,
            int scale) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, x, precision, scale});

        checkClosed();
        updateObject(findColumn(columnName), x, Integer.valueOf(scale), null, precision, false);

        loggerExternal.exiting(getClassNameLogging(), "updateObject");
    }

    /**
     * Updates the designated column with an {@code Object} value.
     *
     * The updater methods are used to update column values in the current row or the insert row. The updater methods do not update the underlying
     * database; instead the {@code updateRow} or {@code insertRow} methods are called to update the database.
     *
     * @param columnName
     *            The name of a column.
     * @param x
     *            the new column value
     * @param precision
     *            the precision of the column
     * @param scale
     *            the scale of the column
     * @param forceEncrypt
     *            If the boolean forceEncrypt is set to true, the query parameter will only be set if the designation column is encrypted and Always
     *            Encrypted is enabled on the connection or on the statement. If the boolean forceEncrypt is set to false, the driver will not force
     *            encryption on parameters.
     * @throws SQLServerException
     *             If any errors occur.
     */
    public void updateObject(String columnName,
            Object x,
            int precision,
            int scale,
            boolean forceEncrypt) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, x, precision, scale, forceEncrypt});

        checkClosed();
        updateObject(findColumn(columnName), x, Integer.valueOf(scale), null, precision, forceEncrypt);

        loggerExternal.exiting(getClassNameLogging(), "updateObject");
    }

    public void updateObject(String columnName,
            Object x) throws SQLServerException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, x});

        checkClosed();
        updateObject(findColumn(columnName), x, null, null, null, false);

        loggerExternal.exiting(getClassNameLogging(), "updateObject");
    }

    public void updateRowId(int columnIndex,
            RowId x) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();

        // Not implemented
        throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported"));
    }

    public void updateRowId(String columnLabel,
            RowId x) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();

        // Not implemented
        throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported"));
    }

    public void updateSQLXML(int columnIndex,
            SQLXML xmlObject) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSQLXML", new Object[] {columnIndex, xmlObject});
        updateSQLXMLInternal(columnIndex, xmlObject);
        loggerExternal.exiting(getClassNameLogging(), "updateSQLXML");
    }

    public void updateSQLXML(String columnLabel,
            SQLXML x) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateSQLXML", new Object[] {columnLabel, x});
        DriverJDBCVersion.checkSupportsJDBC4();
        updateSQLXMLInternal(findColumn(columnLabel), x);
        loggerExternal.exiting(getClassNameLogging(), "updateSQLXML");
    }

    public int getHoldability() throws SQLException {
        loggerExternal.entering(getClassNameLogging(), "getHoldability");

        DriverJDBCVersion.checkSupportsJDBC4();
        checkClosed();

        int holdability =

                // Client-cursored result sets are always holdable because there is
                // no server cursor for the server to close, and no way for the driver
                // to detect the commit anyway...
                (0 == stmt.getServerCursorId()) ? ResultSet.HOLD_CURSORS_OVER_COMMIT :

                // For Yukon and later server-cursored result sets, holdability
                // was determined at statement execution time and does not change.
                        stmt.getExecProps().getHoldability();

        loggerExternal.exiting(getClassNameLogging(), "getHoldability", holdability);

        return holdability;
    }

    /* ----------------------- Update result set ------------------------- */

    public void insertRow() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "insertRow");
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }

        final class InsertRowRPC extends TDSCommand {
            final String tableName;

            InsertRowRPC(String tableName) {
                super("InsertRowRPC", 0);
                this.tableName = tableName;
            }

            final boolean doExecute() throws SQLServerException {
                doInsertRowRPC(this, tableName);
                return true;
            }
        }

        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the concurrency of this ResultSet object is CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();

        if (!isOnInsertRow) {
            SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_mustBeOnInsertRow"), null, true);
        }

        // Determine the table/view into which the row is to be inserted.
        //
        // The logic for doing this is as follows:
        //
        // If values were set for any of the columns in this ResultSet
        // then use the table associated with the first column in the
        // SELECT list whose value was set.
        //
        // If no values were set for any of the columns (insert row with
        // default values) then choose the table name associated with the
        // first updatable column. An updatable column is one that is not
        // a computed expression, not hidden, and has a non-empty table name.
        //
        // If no values were set for any columns and no columns are updatable,
        // then the table name cannot be determined, so error.
        Column tableColumn = null;
        for (int i = 0; i < columns.length; i++) {
            if (columns[i].hasUpdates()) {
                tableColumn = columns[i];
                break;
            }

            if (null == tableColumn && columns[i].isUpdatable())
                tableColumn = columns[i];
        }

        if (null == tableColumn) {
            SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_noColumnParameterValue"), null, true);
        }

        assert tableColumn.isUpdatable();
        assert null != tableColumn.getTableName();

        stmt.executeCommand(new InsertRowRPC(tableColumn.getTableName().asEscapedString()));

        if (UNKNOWN_ROW_COUNT != rowCount)
            ++rowCount;
        loggerExternal.exiting(getClassNameLogging(), "insertRow");
    }

    private void doInsertRowRPC(TDSCommand command,
            String tableName) throws SQLServerException {
        assert 0 != serverCursorId;
        assert null != tableName;
        assert tableName.length() > 0;

        TDSWriter tdsWriter = command.startRequest(TDS.PKT_RPC);
        tdsWriter.writeShort((short) 0xFFFF); // procedure name length -> use ProcIDs
        tdsWriter.writeShort(TDS.PROCID_SP_CURSOR);
        tdsWriter.writeByte((byte) 0);  // RPC procedure option 1
        tdsWriter.writeByte((byte) 0);  // RPC procedure option 2
        tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false);
        tdsWriter.writeRPCInt(null, new Integer(TDS.SP_CURSOR_OP_INSERT), false);
        tdsWriter.writeRPCInt(null, new Integer(fetchBufferGetRow()), false);

        if (hasUpdatedColumns()) {
            tdsWriter.writeRPCStringUnicode(tableName);

            for (int i = 0; i < columns.length; i++)
                columns[i].sendByRPC(tdsWriter, stmt.connection);
        }
        else {
            tdsWriter.writeRPCStringUnicode("");
            tdsWriter.writeRPCStringUnicode("INSERT INTO " + tableName + " DEFAULT VALUES");
        }

        TDSParser.parse(command.startResponse(), command.getLogContext());
    }

    public void updateRow() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "updateRow");
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        final class UpdateRowRPC extends TDSCommand {
            UpdateRowRPC() {
                super("UpdateRowRPC", 0);
            }

            final boolean doExecute() throws SQLServerException {
                doUpdateRowRPC(this);
                return true;
            }
        }

        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the concurrency of this ResultSet object is CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();

        // From JDBC spec:
        // [updateRow] must be called when the cursor is on the current row;
        // an exception will be thrown if it is called with the cursor is on the insert row.
        verifyResultSetIsNotOnInsertRow();
        verifyResultSetHasCurrentRow();

        // Deleted rows cannot be updated.
        verifyCurrentRowIsNotDeleted("R_cantUpdateDeletedRow");

        if (!hasUpdatedColumns()) {
            SQLServerException.makeFromDriverError(stmt.connection, stmt, SQLServerException.getErrString("R_noColumnParameterValue"), null, true);
        }

        try {
            stmt.executeCommand(new UpdateRowRPC());
        }
        finally {
            cancelUpdates();
        }

        updatedCurrentRow = true;
        loggerExternal.exiting(getClassNameLogging(), "updateRow");
    }

    private void doUpdateRowRPC(TDSCommand command) throws SQLServerException {
        assert 0 != serverCursorId;

        TDSWriter tdsWriter = command.startRequest(TDS.PKT_RPC);
        tdsWriter.writeShort((short) 0xFFFF); // procedure name length -> use ProcIDs
        tdsWriter.writeShort(TDS.PROCID_SP_CURSOR);
        tdsWriter.writeByte((byte) 0);  // RPC procedure option 1
        tdsWriter.writeByte((byte) 0);  // RPC procedure option 2
        tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false);
        tdsWriter.writeRPCInt(null, new Integer(TDS.SP_CURSOR_OP_UPDATE | TDS.SP_CURSOR_OP_SETPOSITION), false);
        tdsWriter.writeRPCInt(null, new Integer(fetchBufferGetRow()), false);
        tdsWriter.writeRPCStringUnicode("");

        assert hasUpdatedColumns();

        for (int i = 0; i < columns.length; i++)
            columns[i].sendByRPC(tdsWriter, stmt.connection);

        TDSParser.parse(command.startResponse(), command.getLogContext());
    }

    /** Determines whether there are updated columns in this result set. */
    final boolean hasUpdatedColumns() {
        for (int i = 0; i < columns.length; i++)
            if (columns[i].hasUpdates())
                return true;

        return false;
    }

    public void deleteRow() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "deleteRow");
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        final class DeleteRowRPC extends TDSCommand {
            DeleteRowRPC() {
                super("DeleteRowRPC", 0);
            }

            final boolean doExecute() throws SQLServerException {
                doDeleteRowRPC(this);
                return true;
            }
        }

        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if this method is called on a ResultSet object that is not updatable ...
        verifyResultSetIsUpdatable();

        // ... or when the cursor is before the first row, after the last row, or on the insert row.
        verifyResultSetIsNotOnInsertRow();
        verifyResultSetHasCurrentRow();

        // Deleted rows cannot be deleted.
        verifyCurrentRowIsNotDeleted("R_cantUpdateDeletedRow");

        try {
            stmt.executeCommand(new DeleteRowRPC());
        }
        finally {
            cancelUpdates();
        }

        deletedCurrentRow = true;
        loggerExternal.exiting(getClassNameLogging(), "deleteRow");
    }

    private void doDeleteRowRPC(TDSCommand command) throws SQLServerException {
        assert 0 != serverCursorId;

        TDSWriter tdsWriter = command.startRequest(TDS.PKT_RPC);
        tdsWriter.writeShort((short) 0xFFFF); // procedure name length -> use ProcIDs
        tdsWriter.writeShort(TDS.PROCID_SP_CURSOR);
        tdsWriter.writeByte((byte) 0);  // RPC procedure option 1
        tdsWriter.writeByte((byte) 0);  // RPC procedure option 2
        tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false);
        tdsWriter.writeRPCInt(null, new Integer(TDS.SP_CURSOR_OP_DELETE | TDS.SP_CURSOR_OP_SETPOSITION), false);
        tdsWriter.writeRPCInt(null, new Integer(fetchBufferGetRow()), false);
        tdsWriter.writeRPCStringUnicode("");

        TDSParser.parse(command.startResponse(), command.getLogContext());
    }

    public void refreshRow() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "refreshRow");
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }

        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // This method is not supported for ResultSet objects that are type TYPE_FORWARD_ONLY.
        verifyResultSetIsScrollable();

        // From JDBC spec:
        // Throws SQLException if the concurrency of this ResultSet object is CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();

        // From JDBC spec:
        // Throws SQLException if this method is called when the cursor is on the insert row.
        verifyResultSetIsNotOnInsertRow();

        // Verify that the cursor is not before the first row, after the last row, or on a deleted row.
        verifyResultSetHasCurrentRow();
        verifyCurrentRowIsNotDeleted("R_cantUpdateDeletedRow");

        // From the JDBC spec:
        // [This method] does nothing for [ResultSet objects] that are type TYPE_SCROLL_INSENSITIVE.
        //
        // Included in that definition is result sets for which the server was asked to return a server cursor,
        // but ended up returning the results directly instead.
        if (ResultSet.TYPE_SCROLL_INSENSITIVE == stmt.getResultSetType() || 0 == serverCursorId)
            return;

        // From JDBC spec:
        // If refreshRow is called after calling updater [methods] but before calling updateRow,
        // then the updates made to the row are lost.
        cancelUpdates();

        doRefreshRow();
        loggerExternal.exiting(getClassNameLogging(), "refreshRow");
    }

    private void doRefreshRow() throws SQLServerException {
        assert hasCurrentRow();

        // Save off the current row offset into the fetch buffer so that we can attempt to
        // restore to that position after refetching.
        int fetchBufferSavedRow = fetchBufferGetRow();

        // Refresh all the rows in the fetch buffer. This is allowed by the JDBC spec:
        // If the fetch size is greater than one, the driver may refetch multiple rows at once.
        doServerFetch(TDS.FETCH_REFRESH, 0, 0);

        // Scroll back to the current row. Note that with DYNAMIC cursors, there is no guarantee
        // that the contents of the fetch buffer after a refresh look anything like they did before
        // the refresh -- rows may have been added, modified, or deleted -- so the current row
        // may not end up where it was before the refresh.
        int fetchBufferRestoredRow = 0;
        while (fetchBufferRestoredRow < fetchBufferSavedRow && (isForwardOnly() ? fetchBufferNext() : scrollWindow.next(this))) {
            ++fetchBufferRestoredRow;
        }

        if (fetchBufferRestoredRow < fetchBufferSavedRow) {
            currentRow = AFTER_LAST_ROW;
            return;
        }

        // Finally, ensure that we've cleared the flag that the current row was updated. This ensures
        // that a subsequent getter doesn't do another unnecessary refresh when called.
        updatedCurrentRow = false;
    }

    private void cancelUpdates() {
        if (!isOnInsertRow)
            clearColumnsValues();
    }

    public void cancelRowUpdates() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "cancelRowUpdates");
        checkClosed();

        // From JDBC spec:
        // Throws SQLException if this method is called when the cursor is on the insert row or if
        // this ResultSet object has a concurrency of CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();
        verifyResultSetIsNotOnInsertRow();

        cancelUpdates();
        loggerExternal.exiting(getClassNameLogging(), "cancelRowUpdates");
    }

    public void moveToInsertRow() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "moveToInsertRow");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the concurrency of this ResultSet object is CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();

        cancelUpdates();
        isOnInsertRow = true;
        loggerExternal.exiting(getClassNameLogging(), "moveToInsertRow");
    }

    public void moveToCurrentRow() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "moveToCurrentRow");
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + logCursorState());

        checkClosed();

        // From JDBC spec:
        // Throws SQLException if the concurrency of this ResultSet object is CONCUR_READ_ONLY.
        verifyResultSetIsUpdatable();

        // Note that we don't verify that the row is on the insert row. From the JDBC spec:
        // This method should be called only when the cursor is on the insert row
        // and has no effect if the cursor is not on the insert row.

        cancelInsert();
        loggerExternal.exiting(getClassNameLogging(), "moveToCurrentRow");

    }

    /* L0 */ public java.sql.Statement getStatement() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "getStatement");
        checkClosed();
        loggerExternal.exiting(getClassNameLogging(), "getStatement", stmt);
        return stmt;
    }

    /* JDBC 3.0 */

    public void updateClob(int columnIndex,
            Clob clobValue) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateClob", new Object[] {columnIndex, clobValue});

        checkClosed();
        updateValue(columnIndex, JDBCType.CLOB, clobValue, JavaType.CLOB, false);

        loggerExternal.exiting(getClassNameLogging(), "updateClob");
    }

    public void updateClob(int columnIndex,
            Reader reader) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateClob", new Object[] {columnIndex, reader});

        checkClosed();
        updateStream(columnIndex, StreamType.CHARACTER, reader, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateClob");
    }

    public void updateClob(int columnIndex,
            Reader reader,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateClob", new Object[] {columnIndex, reader, length});

        checkClosed();
        updateStream(columnIndex, StreamType.CHARACTER, reader, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateClob");
    }

    public void updateClob(String columnName,
            Clob clobValue) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateClob", new Object[] {columnName, clobValue});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.CLOB, clobValue, JavaType.CLOB, false);

        loggerExternal.exiting(getClassNameLogging(), "updateClob");
    }

    public void updateClob(String columnLabel,
            Reader reader) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateClob", new Object[] {columnLabel, reader});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.CHARACTER, reader, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateClob");
    }

    public void updateClob(String columnLabel,
            Reader reader,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateClob", new Object[] {columnLabel, reader, length});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.CHARACTER, reader, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateClob");
    }

    public void updateNClob(int columnIndex,
            NClob nClob) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateClob", new Object[] {columnIndex, nClob});

        checkClosed();
        updateValue(columnIndex, JDBCType.NCLOB, nClob, JavaType.NCLOB, false);

        loggerExternal.exiting(getClassNameLogging(), "updateNClob");
    }

    public void updateNClob(int columnIndex,
            Reader reader) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNClob", new Object[] {columnIndex, reader});

        checkClosed();
        updateStream(columnIndex, StreamType.NCHARACTER, reader, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateNClob");
    }

    public void updateNClob(int columnIndex,
            Reader reader,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNClob", new Object[] {columnIndex, reader, length});

        checkClosed();
        updateStream(columnIndex, StreamType.NCHARACTER, reader, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateNClob");
    }

    public void updateNClob(String columnLabel,
            NClob nClob) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNClob", new Object[] {columnLabel, nClob});

        checkClosed();
        updateValue(findColumn(columnLabel), JDBCType.NCLOB, nClob, JavaType.NCLOB, false);

        loggerExternal.exiting(getClassNameLogging(), "updateNClob");
    }

    public void updateNClob(String columnLabel,
            Reader reader) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNClob", new Object[] {columnLabel, reader});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.NCHARACTER, reader, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateNClob");
    }

    public void updateNClob(String columnLabel,
            Reader reader,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateNClob", new Object[] {columnLabel, reader, length});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.NCHARACTER, reader, JavaType.READER, length);

        loggerExternal.exiting(getClassNameLogging(), "updateNClob");
    }

    public void updateBlob(int columnIndex,
            Blob blobValue) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBlob", new Object[] {columnIndex, blobValue});

        checkClosed();
        updateValue(columnIndex, JDBCType.BLOB, blobValue, JavaType.BLOB, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBlob");
    }

    public void updateBlob(int columnIndex,
            InputStream inputStream) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBlob", new Object[] {columnIndex, inputStream});

        checkClosed();
        updateStream(columnIndex, StreamType.BINARY, inputStream, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateBlob");
    }

    public void updateBlob(int columnIndex,
            InputStream inputStream,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBlob", new Object[] {columnIndex, inputStream, length});

        checkClosed();
        updateStream(columnIndex, StreamType.BINARY, inputStream, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateBlob");
    }

    public void updateBlob(String columnName,
            Blob blobValue) throws SQLException {
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBlob", new Object[] {columnName, blobValue});

        checkClosed();
        updateValue(findColumn(columnName), JDBCType.BLOB, blobValue, JavaType.BLOB, false);

        loggerExternal.exiting(getClassNameLogging(), "updateBlob");
    }

    public void updateBlob(String columnLabel,
            InputStream inputStream) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBlob", new Object[] {columnLabel, inputStream});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.BINARY, inputStream, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH);

        loggerExternal.exiting(getClassNameLogging(), "updateBlob");
    }

    public void updateBlob(String columnLabel,
            InputStream inputStream,
            long length) throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC4();
        if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
            loggerExternal.entering(getClassNameLogging(), "updateBlob", new Object[] {columnLabel, inputStream, length});

        checkClosed();
        updateStream(findColumn(columnLabel), StreamType.BINARY, inputStream, JavaType.INPUTSTREAM, length);

        loggerExternal.exiting(getClassNameLogging(), "updateBlob");
    }

    /* L3 */ public void updateArray(int columnIndex,
            Array x) throws SQLServerException {
        stmt.NotImplemented();
    }

    /* L3 */ public void updateArray(java.lang.String columnName,
            Array x) throws SQLServerException {
        stmt.NotImplemented();
    }

    /* L3 */ public void updateRef(int columnIndex,
            Ref x) throws SQLServerException {
        stmt.NotImplemented();
    }

    /* L3 */ public void updateRef(java.lang.String columnName,
            Ref x) throws SQLServerException {
        stmt.NotImplemented();
    }

    /* L3 */ public java.net.URL getURL(int columnIndex) throws SQLServerException {
        stmt.NotImplemented();
        return null;
    }

    /* L3 */ public java.net.URL getURL(String sColumn) throws SQLServerException {
        stmt.NotImplemented();
        return null;
    }

    /** Absolute position in this ResultSet where the fetch buffer starts */
    private int numFetchedRows;

    /**
     * Fetch buffer that provides a source of rows to this ResultSet.
     *
     * The FetchBuffer class is different conceptually from the ScrollWindow class. The latter provides indexing and arbitrary scrolling over rows
     * provided by the fetch buffer. The fetch buffer itself just provides the rows and supports rewinding back to the start of the buffer. When
     * server cursors are involved, the fetch buffer is typically the same size as the scroll window. However, with client side scrollable cursors,
     * the fetch buffer would contain all of the rows in the result set, but the scroll window would only contain some of them (determined by
     * ResultSet.setFetchSize).
     *
     * The fetch buffer contains 0 or more ROW tokens followed by a DONE (cmd=SELECT, 0xC1) token indicating the number of rows in the fetch buffer.
     *
     * For client-cursored result sets, the fetch buffer contains all of the rows in the result set, and the DONE token indicates the total number of
     * rows in the result set, though we don't use that information, as we always count rows as we encounter them.
     */
    private final class FetchBuffer {
        private final class FetchBufferTokenHandler extends TDSTokenHandler {
            FetchBufferTokenHandler() {
                super("FetchBufferTokenHandler");
            }

            // Even though the cursor fetch RPC call specified the "no metadata" option,
            // the server still returns a COLMETADATA_TOKEN containing the magic NoMetaData
            // value that we need to read through.
            boolean onColMetaData(TDSReader tdsReader) throws SQLServerException {
                (new StreamColumns(Util.shouldHonorAEForRead(stmt.stmtColumnEncriptionSetting, stmt.connection))).setFromTDS(tdsReader);
                return true;
            }

            boolean onRow(TDSReader tdsReader) throws SQLServerException {
                ensureStartMark();

                // Consume the ROW token, leaving tdsReader at the start of
                // this row's column values.
                if (TDS.TDS_ROW != tdsReader.readUnsignedByte())
                    assert false;
                fetchBufferCurrentRowType = RowType.ROW;
                return false;
            }

            boolean onNBCRow(TDSReader tdsReader) throws SQLServerException {
                ensureStartMark();

                // Consume the NBCROW token, leaving tdsReader at the start of
                // nullbitmap.
                if (TDS.TDS_NBCROW != tdsReader.readUnsignedByte())
                    assert false;

                fetchBufferCurrentRowType = RowType.NBCROW;
                return false;
            }

            boolean onDone(TDSReader tdsReader) throws SQLServerException {
                ensureStartMark();

                // Consume the done token
                StreamDone doneToken = new StreamDone();
                doneToken.setFromTDS(tdsReader);

                // Done with all the rows in this fetch buffer and done with parsing
                // unless it's a server cursor, in which case there is a RETSTAT and
                // another DONE token to follow.
                done = true;
                return 0 != serverCursorId;
            }

            boolean onRetStatus(TDSReader tdsReader) throws SQLServerException {
                // Check the return status for the bit indicating that
                // "counter-intuitive" cursor behavior has happened and
                // that a fixup is necessary.
                StreamRetStatus retStatusToken = new StreamRetStatus();
                retStatusToken.setFromTDS(tdsReader);
                needsServerCursorFixup = (2 == retStatusToken.getStatus());
                return true;
            }

            void onEOF(TDSReader tdsReader) throws SQLServerException {
                super.onEOF(tdsReader);
                done = true;
            }
        }

        private final FetchBufferTokenHandler fetchBufferTokenHandler = new FetchBufferTokenHandler();

        /** Location in the TDS response of the start of the fetch buffer */
        private TDSReaderMark startMark;

        final void clearStartMark() {
            startMark = null;
        }

        private RowType fetchBufferCurrentRowType = RowType.UNKNOWN;
        private boolean done;
        private boolean needsServerCursorFixup;

        final boolean needsServerCursorFixup() {
            return needsServerCursorFixup;
        }

        FetchBuffer() {
            init();
        }

        final void ensureStartMark() {
            if (null == startMark && !isForwardOnly()) {
                if (logger.isLoggable(java.util.logging.Level.FINEST))
                    logger.finest(toString() + " Setting fetch buffer start mark");

                startMark = tdsReader.mark();
            }
        }

        /**
         * Repositions the fetch buffer back to the beginning.
         */
        final void reset() {
            assert null != tdsReader;
            assert null != startMark;

            tdsReader.reset(startMark);
            fetchBufferCurrentRowType = RowType.UNKNOWN;
            done = false;
        }

        /**
         * Initializes the fetch buffer with new contents and optionally sets a TDSReaderMark at the start of the fetch buffer to allow the fetch
         * buffer to be scrolled back to the beginning.
         */
        final void init() {
            startMark = (0 == serverCursorId && !isForwardOnly()) ? tdsReader.mark() : null;
            fetchBufferCurrentRowType = RowType.UNKNOWN;
            done = false;
            needsServerCursorFixup = false;
        }

        /**
         * Moves to the next row in the fetch buffer.
         */
        final RowType nextRow() throws SQLServerException {
            fetchBufferCurrentRowType = RowType.UNKNOWN;

            while (null != tdsReader && !done && fetchBufferCurrentRowType.equals(RowType.UNKNOWN))
                TDSParser.parse(tdsReader, fetchBufferTokenHandler);

            if (fetchBufferCurrentRowType.equals(RowType.UNKNOWN) && null != fetchBufferTokenHandler.getDatabaseError()) {
                SQLServerException.makeFromDatabaseError(stmt.connection, null, fetchBufferTokenHandler.getDatabaseError().getMessage(),
                        fetchBufferTokenHandler.getDatabaseError(), false);
            }

            return fetchBufferCurrentRowType;
        }
    }

    private final class CursorFetchCommand extends TDSCommand {
        private final int serverCursorId;
        private int fetchType;
        private int startRow;
        private int numRows;

        CursorFetchCommand(int serverCursorId,
                int fetchType,
                int startRow,
                int numRows) {
            super("doServerFetch", stmt.queryTimeout);
            this.serverCursorId = serverCursorId;
            this.fetchType = fetchType;
            this.startRow = startRow;
            this.numRows = numRows;
        }

        final boolean doExecute() throws SQLServerException {
            TDSWriter tdsWriter = startRequest(TDS.PKT_RPC);
            tdsWriter.writeShort((short) 0xFFFF); // procedure name length -> use ProcIDs
            tdsWriter.writeShort(TDS.PROCID_SP_CURSORFETCH);
            tdsWriter.writeByte(TDS.RPC_OPTION_NO_METADATA);
            tdsWriter.writeByte((byte) 0);  // RPC procedure option 2
            tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false);
            tdsWriter.writeRPCInt(null, new Integer(fetchType), false);
            tdsWriter.writeRPCInt(null, new Integer(startRow), false);
            tdsWriter.writeRPCInt(null, new Integer(numRows), false);

            // To free up the thread on the server that is feeding us these results,
            // read the entire response off the wire UNLESS this is a forward only
            // updatable result set AND responseBuffering was explicitly set to adaptive
            // at the Statement level. This override is the only way that apps
            // can do a forward only updatable pass through a ResultSet with large
            // data values.
            tdsReader = startResponse(isForwardOnly() && CONCUR_READ_ONLY != stmt.resultSetConcurrency
                    && stmt.getExecProps().wasResponseBufferingSet() && stmt.getExecProps().isResponseBufferingAdaptive());

            return false;
        }

        final void processResponse(TDSReader responseTDSReader) throws SQLServerException {
            tdsReader = responseTDSReader;
            discardFetchBuffer();
        }
    }

    /**
     * Position a server side cursor.
     *
     * @param fetchType
     *            The type of fetch
     * @param startRow
     *            The starting row
     * @param numRows
     *            The number of rows to fetch
     * @exception SQLServerException
     *                The cursor was invalid.
     */
    final void doServerFetch(int fetchType,
            int startRow,
            int numRows) throws SQLServerException {
        if (logger.isLoggable(java.util.logging.Level.FINER))
            logger.finer(toString() + " fetchType:" + fetchType + " startRow:" + startRow + " numRows:" + numRows);

        // Discard the current fetch buffer contents
        discardFetchBuffer();

        // Reinitialize the fetch buffer
        fetchBuffer.init();

        // Fetch the requested block of rows from the server
        CursorFetchCommand cursorFetch = new CursorFetchCommand(serverCursorId, fetchType, startRow, numRows);
        stmt.executeCommand(cursorFetch);

        numFetchedRows = 0;
        resultSetCurrentRowType = RowType.UNKNOWN;
        areNullCompressedColumnsInitialized = false;
        lastColumnIndex = 0;

        // If necessary, resize the scroll window to the new fetch size
        if (null != scrollWindow && TDS.FETCH_REFRESH != fetchType)
            scrollWindow.resize(fetchSize);

        // Correct for SQL Server's "counter-intuitive" behavior which positions the cursor
        // on the first row of the result set when a negative move would have logically
        // positioned the cursor before the first row instead. When this happens, the server
        // returns the first block of rows, which is indistinguishable from a regular request
        // for the first block of rows except for a flag set in the return status to indicate
        // what happened. When this behavior does happen, force the cursor to move to before
        // the first row. See the Engine Cursors Functional Specification for all the details....
        if (numRows < 0 || startRow < 0) {
            // Scroll past all the returned rows, caching in the scroll window as we go.
            try {
                while (scrollWindow.next(this))
                    ;
            }
            catch (SQLException e) {
                // If there is a row error in the results, don't throw an exception from here.
                // Ignore it for now and defer the exception until the app encounters the
                // error through normal cursor movement.
                if (logger.isLoggable(java.util.logging.Level.FINER))
                    logger.finer(toString() + " Ignored exception from row error during server cursor fixup: " + e.getMessage());
            }

            // Force the cursor to move to before the first row if necessary.
            if (fetchBuffer.needsServerCursorFixup()) {
                doServerFetch(TDS.FETCH_FIRST, 0, 0);
                return;
            }

            // Put the scroll window back before the first row.
            scrollWindow.reset();
        }
    }

    /**
     * Discards the contents of the current fetch buffer.
     *
     * This method ensures that the contents of the current fetch buffer have been completely read from the TDS channel, processed, and discarded.
     *
     * Note that exceptions resulting from database errors, such as row errors or transaction rollbacks, and from I/O errors, such as a closed
     * connection, are caught, logged, and ignored. The expectation is that callers of this method just want the fetch buffer cleared out and do not
     * care about what errors may have occurred when it was last populated. If the connection is closed while discarding the fetch buffer, then the
     * fetch buffer is considered to be discarded.
     */
    private void discardFetchBuffer() {
        // Clear the TDSReader mark at the start of the fetch buffer
        fetchBuffer.clearStartMark();

        // Clear all row TDSReader marks in the scroll window
        if (null != scrollWindow)
            scrollWindow.clear();

        // Once there are no TDSReader marks left referring to the fetch buffer
        // contents, process the remainder of the current row and all subsequent rows.
        try {
            while (fetchBufferNext())
                ;
        }
        catch (SQLServerException e) {
            if (logger.isLoggable(java.util.logging.Level.FINER))
                logger.finer(this + " Encountered exception discarding fetch buffer: " + e.getMessage());
        }
    }

    /**
     * Close a server side cursor and free up its resources in the database.
     *
     * If closing fails for any reason then the cursor is considered closed.
     */
    final void closeServerCursor() {
        if (0 == serverCursorId)
            return;

        // If the connection is already closed, don't bother trying to close the server cursor.
        // We won't be able to, and it's already closed on the server anyway.
        if (stmt.connection.isSessionUnAvailable()) {
            if (logger.isLoggable(java.util.logging.Level.FINER))
                logger.finer(this + ": Not closing cursor:" + serverCursorId + "; connection is already closed.");
        }
        else {
            if (logger.isLoggable(java.util.logging.Level.FINER))
                logger.finer(toString() + " Closing cursor:" + serverCursorId);

            final class CloseServerCursorCommand extends UninterruptableTDSCommand {
                CloseServerCursorCommand() {
                    super("closeServerCursor");
                }

                final boolean doExecute() throws SQLServerException {
                    TDSWriter tdsWriter = startRequest(TDS.PKT_RPC);
                    tdsWriter.writeShort((short) 0xFFFF); // procedure name length -> use ProcIDs
                    tdsWriter.writeShort(TDS.PROCID_SP_CURSORCLOSE);
                    tdsWriter.writeByte((byte) 0);  // RPC procedure option 1
                    tdsWriter.writeByte((byte) 0);  // RPC procedure option 2
                    tdsWriter.writeRPCInt(null, new Integer(serverCursorId), false);
                    TDSParser.parse(startResponse(), getLogContext());
                    return true;
                }
            }

            // Try to close the server cursor. Any failure is caught, logged, and ignored.
            try {
                stmt.executeCommand(new CloseServerCursorCommand());
            }
            catch (SQLServerException e) {
                if (logger.isLoggable(java.util.logging.Level.FINER))
                    logger.finer(toString() + " Ignored error closing cursor:" + serverCursorId + " " + e.getMessage());
            }

            if (logger.isLoggable(java.util.logging.Level.FINER))
                logger.finer(toString() + " Closed cursor:" + serverCursorId);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy