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

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

The newest version!
/*
 * 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.Connection;
import java.sql.NClob;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLType;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification;


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


/**
 * Defines the Top-level JDBC ResultSet implementation.
 */
public class SQLServerResultSet implements ISQLServerResultSet, java.io.Serializable {

    /**
     * Always refresh SerialVersionUID when prompted
     */
    private static final long serialVersionUID = -1624082547992040463L;

    /**
     * SQL State Invalid descriptor index
     */
    private static final String SQLSTATE_INVALID_DESCRIPTOR_INDEX = "07009";

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

    private static final String ACTIVITY_ID = " ActivityId: ";

    /** trace ID */
    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");

    @Override
    public String toString() {
        return traceID;
    }

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

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

    /** logging classname */
    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 SQLServerResultSetMetaData metaData;

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

    /** server cursor id */
    private final int serverCursorId;

    /**
     * Returns the server cursor id
     * 
     * @return server cursor id
     */
    protected int getServerCursorId() {
        return 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 transient Closeable activeStream;

    /** active LOB */
    private SQLServerLob activeLOB;

    /**
     * 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;

    /** current row */
    private int currentRow = BEFORE_FIRST_ROW;

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

    /** Column name hash map for caching */
    private final Map columnNames = new HashMap<>();

    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;

    /** row count */
    private int rowCount;

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

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

    /* Returns 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;

    TDSReader getTDSReader() {
        return tdsReader;
    }

    /** fetch buffer */
    private final transient FetchBuffer fetchBuffer;

    @Override
    public SensitivityClassification getSensitivityClassification() {
        return tdsReader.sensitivityClassification;
    }

    /**
     * Constructs a SQLServerResultSet.
     * 
     * @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);
            }

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

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

            @Override
            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;
            }

            @Override
            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;
            }

            @Override
            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");
            }

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

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

            @Override
            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;
            }

            @Override
            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;

                // decrementUnprocessedResponseCount() outside the "if" is not necessary here. It will over decrement if added.

                short status = tdsReader.peekStatusFlag();
                if ((status & TDS.DONE_ERROR) != 0 || (status & TDS.DONE_SRVERROR) != 0) {
                    StreamDone doneToken = new StreamDone();
                    doneToken.setFromTDS(tdsReader);
                    if (doneToken.isFinal()) {
                        stmt.connection.getSessionRecovery().decrementUnprocessedResponseCount();
                    }
                    SQLServerError databaseError = this.getDatabaseError();
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_serverError"));
                    Object[] msgArgs = {status, (databaseError != null) ? databaseError.getErrorMessage() : ""};

                    if (null != databaseError) {
                        SQLServerException.makeFromDatabaseError(stmt.connection, null, form.format(msgArgs),
                                databaseError, false);
                    } else {
                        SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), null,
                                false);
                    }
                }

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

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

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

    /** row error exception */
    private SQLServerException rowErrorException = null;

    /**
     * Checks if the result set is closed
     * 
     * @throws SQLServerException
     */
    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;
    }

    @Override
    public boolean isClosed() throws SQLException {
        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 SQLException
     *         if the result set is forward only.
     * @throws SQLFeatureNotSupportedException
     */
    private void throwNotScrollable() throws SQLException {
        SQLServerException.makeFromDriverError(stmt.connection, this,
                SQLServerException.getErrString("R_requestedOpNotSupportedOnForward"), null, true);
    }

    /**
     * Check if type is ForwardOnly
     * 
     * @return if type is ForwardOnly
     */
    protected 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 SQLException {
        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();
    }

    /**
     * Returns 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 = {index};
            SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs),
                    SQLSTATE_INVALID_DESCRIPTOR_INDEX, 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);
    }

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

    @Override
    public void close() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "close");
        if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
            loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().toString());
        }
        closeInternal();
        loggerExternal.exiting(getClassNameLogging(), "close");
    }

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

        Integer value = columnNames.get(userProvidedColumnName);
        if (null != value) {
            return value;
        }

        // 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.
        for (int i = 0; i < columns.length; i++) {
            if (columns[i].getColumnName().equals(userProvidedColumnName)) {
                columnNames.put(userProvidedColumnName, i + 1);
                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 (int i = 0; i < columns.length; i++) {
            if (columns[i].getColumnName().equalsIgnoreCase(userProvidedColumnName)) {
                columnNames.put(userProvidedColumnName, i + 1);
                loggerExternal.exiting(getClassNameLogging(), "findColumn", i + 1);
                return i + 1;
            }
        }
        MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnName"));
        Object[] msgArgs = {userProvidedColumnName};
        SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs),
                SQLSTATE_INVALID_DESCRIPTOR_INDEX, 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 {
                fillLOBs();
                activeStream.close();
            } catch (IOException e) {
                SQLServerException.makeFromDriverError(null, null, e.getMessage(), null, true);
            } finally {
                activeStream = null;
            }
        }

        return columns[columnIndex - 1];
    }

    /**
     * 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++, columnNo++) {
                    if ((byteValue & (1 << bitNo)) != 0) {
                        this.columns[columnNo].initFromCompressedNull();
                    }
                }
            }
            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);
    }

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

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

    private void moverInit() {
        fillLOBs();
        cancelInsert();
        cancelUpdates();
    }

    @Override
    public boolean relative(int rows) throws SQLException {
        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 current 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);
    }

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

    /**
     * Moves the cursor to the first row of this ResultSet object initially, then subsequent calls move the cursor to
     * the second row, the third row, and so on.
     *
     * @return false when there are no more rows to read
     */
    @Override
    public boolean next() throws SQLServerException {
        loggerExternal.entering(getClassNameLogging(), "next");
        if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
            loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().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;

        // Read SQL Warnings at the end of ResultSet
        if (stmt.resultsReader().peekTokenType() == TDS.TDS_MSG) {
            stmt.startResults();
            stmt.getNextResult(false);
        }

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

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

    /**
     * Returns if the cursor is before the first row in this result set.
     * 
     * @return true if the cursor is before the first row in this result set, returns false otherwise or if the result
     *         set contains no rows.
     */
    @Override
    public boolean isBeforeFirst() throws SQLException {
        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;
    }

    @Override
    public boolean isAfterLast() throws SQLException {
        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;
    }

    /**
     * Returns whether the cursor is on the first row of 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.
     * 
     * 

* Note:Support for the isFirst method is optional for ResultSets with a * result set type of TYPE_FORWARD_ONLY * * @return true if the cursor is on the first row; false otherwise * * @exception SQLException * if a database access error occurs or this method is called on a closed result set * * @since 1.2 */ @Override public boolean isFirst() throws SQLException { 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; } /** * Returns whether the cursor is on the last row of this ResultSet object. Note: * Calling the method isLast may be expensive because the JDBC driver might need to fetch ahead one row * in order to determine whether the current row is the last row in the result set. * *

* 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. *

* * Note: Support for the isLast method is optional for ResultSets with a * result set type of TYPE_FORWARD_ONLY * * @return true if the cursor is on the last row; false otherwise * * @exception SQLException * if a database access error occurs or this method is called on a closed result set * * @since 1.2 */ @Override public boolean isLast() throws SQLException { 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; } @Override public void beforeFirst() throws SQLException { loggerExternal.entering(getClassNameLogging(), "beforeFirst"); if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) { loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().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; } @Override public void afterLast() throws SQLException { loggerExternal.entering(getClassNameLogging(), "afterLast"); if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) { loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().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; false if there are no rows in the result * set * * @exception SQLException * if a database access error occurs; this method is called on a closed result set or the result set type * is TYPE_FORWARD_ONLY * * @since 1.2 */ @Override public boolean first() throws SQLException { 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; false if there are no rows in the result * set * * @exception SQLException * if a database access error occurs; this method is called on a closed result set or the result set type * is TYPE_FORWARD_ONLY * * @since 1.2 */ @Override public boolean last() throws SQLException { 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; } @Override public int getRow() throws SQLException { 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 given row number 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. *

* *

* If the row number is positive, the cursor moves to the given row number with respect to the beginning of the * result set. The first row is row 1, the second is row 2, and so on. * *

* If the given row number is negative, the cursor moves to an absolute row position with respect to the end of the * result set. For example, calling the method absolute(-1) positions the cursor on the last row; * calling the method absolute(-2) moves the cursor to the next-to-last row, and so on. * *

* If the row number specified is zero, the cursor is moved to before the first row. * *

* An attempt to position the cursor beyond the first/last row in the result set leaves the cursor before the first * row or after the last row. * *

* Note: Calling absolute(1) is the same as calling first(). Calling * absolute(-1) is the same as calling last(). * * @param row * the number of the row to which the cursor should move. A value of zero indicates that the cursor will be * positioned before the first row; a positive number indicates the row number counting from the beginning of * the result set; a negative number indicates the row number counting from the end of the result set * * @return true if the cursor is moved to a position in this ResultSet object; * false if the cursor is before the first row or after the last row * * @exception SQLException * if a database access error occurs; this method is called on a closed result set or the result set type * is TYPE_FORWARD_ONLY * * @since 1.2 */ @Override public boolean absolute(int row) throws SQLException { loggerExternal.entering(getClassNameLogging(), "absolute"); if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) { loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().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; default: // 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 now positioned on a valid row; false if the cursor is * positioned before the first row * * @exception SQLException * if a database access error occurs; this method is called on a closed result set or the result set type * is TYPE_FORWARD_ONLY * * @since 1.2 */ @Override public boolean previous() throws SQLException { 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() { for (Column column : columns) column.cancelUpdates(); } @Override public SQLWarning getWarnings() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getWarnings"); loggerExternal.exiting(getClassNameLogging(), "getWarnings", null); return null; } @Override public void setFetchDirection(int direction) throws SQLException { 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 = {direction}; SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), null, false); } fetchDirection = direction; loggerExternal.exiting(getClassNameLogging(), "setFetchDirection"); } @Override public int getFetchDirection() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getFetchDirection"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getFetchDirection", fetchDirection); return fetchDirection; } @Override 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"); } @Override public int getFetchSize() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getFetchSize"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getFloat", fetchSize); return fetchSize; } @Override public int getType() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getType"); checkClosed(); int value = stmt.getResultSetType(); loggerExternal.exiting(getClassNameLogging(), "getType", value); return value; } @Override 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); fillLOBs(); 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, stmt); lastValueWasNull = (null == o); return o; } void setInternalVariantType(int columnIndex, SqlVariant type) throws SQLServerException { getterGetColumn(columnIndex).setInternalVariant(type); } SqlVariant getVariantInternalType(int columnIndex) throws SQLServerException { return getterGetColumn(columnIndex).getInternalVariant(); } 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; } private void configureLobs(SQLServerLob lob) throws SQLServerException { if (null != stmt) { Connection c = stmt.getConnection(); if (c instanceof ISQLServerConnection) { if (null != c && !((ISQLServerConnection) c).getDelayLoadingLobs() && null != lob) { lob.setDelayLoadingLob(); } } } activeLOB = lob; } @Override 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; } @Override 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 */ @Deprecated(since = "6.5.4") @Override 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 */ @Deprecated(since = "6.5.4") @Override 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; } @Override 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; } @Override 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; } @Override 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 : false; } @Override 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 : false; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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 : 0; } @Override 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 : 0; } @Override 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 : 0; } @Override 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 : 0; } @Override public Geometry getGeometry(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnIndex); checkClosed(); Geometry value = (Geometry) getValue(columnIndex, JDBCType.GEOMETRY); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); return value; } @Override public Geometry getGeometry(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnName); checkClosed(); Geometry value = (Geometry) getValue(findColumn(columnName), JDBCType.GEOMETRY); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); return value; } @Override public Geography getGeography(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnIndex); checkClosed(); Geography value = (Geography) getValue(columnIndex, JDBCType.GEOGRAPHY); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); return value; } @Override public Geography getGeography(String columnName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getFloat", columnName); checkClosed(); Geography value = (Geography) getValue(findColumn(columnName), JDBCType.GEOGRAPHY); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); return value; } @Override 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 : 0; } @Override 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 : 0; } @Override 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 : 0; } @Override 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 : 0; } @Override 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; } @Override 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; } @Override public T getObject(int columnIndex, Class type) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getObject", columnIndex); checkClosed(); Object returnValue; if (type == String.class) { returnValue = getString(columnIndex); } else if (type == Byte.class) { byte byteValue = getByte(columnIndex); returnValue = wasNull() ? null : byteValue; } else if (type == Short.class) { short shortValue = getShort(columnIndex); returnValue = wasNull() ? null : shortValue; } else if (type == Integer.class) { int intValue = getInt(columnIndex); returnValue = wasNull() ? null : intValue; } else if (type == Long.class) { long longValue = getLong(columnIndex); returnValue = wasNull() ? null : longValue; } else if (type == BigDecimal.class) { returnValue = getBigDecimal(columnIndex); } else if (type == Boolean.class) { boolean booleanValue = getBoolean(columnIndex); returnValue = wasNull() ? null : booleanValue; } else if (type == java.sql.Date.class) { returnValue = getDate(columnIndex); } else if (type == java.sql.Time.class) { returnValue = getTime(columnIndex); } else if (type == java.sql.Timestamp.class) { returnValue = getTimestamp(columnIndex); } else if (type == java.time.LocalDateTime.class || type == java.time.LocalDate.class || type == java.time.LocalTime.class) { java.time.LocalDateTime ldt = getLocalDateTime(columnIndex); if (null == ldt) { returnValue = null; } else { if (type == java.time.LocalDateTime.class) { returnValue = ldt; } else if (type == java.time.LocalDate.class) { returnValue = ldt.toLocalDate(); } else { returnValue = ldt.toLocalTime(); } } } else if (type == java.time.OffsetDateTime.class) { microsoft.sql.DateTimeOffset dateTimeOffset = getDateTimeOffset(columnIndex); if (dateTimeOffset == null) { returnValue = null; } else { returnValue = dateTimeOffset.getOffsetDateTime(); } } else if (type == java.time.OffsetTime.class) { microsoft.sql.DateTimeOffset dateTimeOffset = getDateTimeOffset(columnIndex); if (dateTimeOffset == null) { returnValue = null; } else { returnValue = dateTimeOffset.getOffsetDateTime().toOffsetTime(); } } else if (type == microsoft.sql.DateTimeOffset.class) { returnValue = getDateTimeOffset(columnIndex); } else if (type == UUID.class) { // read binary, avoid string allocation and parsing byte[] guid = getBytes(columnIndex); returnValue = guid != null ? Util.readGUIDtoUUID(guid) : null; } else if (type == SQLXML.class) { returnValue = getSQLXML(columnIndex); } else if (type == Blob.class) { returnValue = getBlob(columnIndex); } else if (type == Clob.class) { returnValue = getClob(columnIndex); } else if (type == NClob.class) { returnValue = getNClob(columnIndex); } else if (type == byte[].class) { returnValue = getBytes(columnIndex); } else if (type == Float.class) { float floatValue = getFloat(columnIndex); returnValue = wasNull() ? null : floatValue; } else if (type == Double.class) { double doubleValue = getDouble(columnIndex); returnValue = wasNull() ? null : doubleValue; } else { // if the type is not supported the specification says the should // a SQLException instead of SQLFeatureNotSupportedException MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedConversionTo")); Object[] msgArgs = {type}; throw new SQLServerException(form.format(msgArgs), SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET, null); } loggerExternal.exiting(getClassNameLogging(), "getObject", columnIndex); return type.cast(returnValue); } @Override 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; } @Override public T getObject(String columnName, Class type) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getObject", columnName); checkClosed(); T value = getObject(findColumn(columnName), type); loggerExternal.exiting(getClassNameLogging(), "getObject", value); return value; } @Override 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 : 0; } @Override 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 : 0; } @Override 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; } @Override 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; } @Override public String getNString(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNString", columnIndex); checkClosed(); String value = (String) getValue(columnIndex, JDBCType.NCHAR); loggerExternal.exiting(getClassNameLogging(), "getNString", value); return value; } @Override public String getNString(String columnLabel) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNString", columnLabel); checkClosed(); String value = (String) getValue(findColumn(columnLabel), JDBCType.NCHAR); loggerExternal.exiting(getClassNameLogging(), "getNString", value); return value; } @Override public String getUniqueIdentifier(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getUniqueIdentifier", columnIndex); checkClosed(); String value = (String) getValue(columnIndex, JDBCType.GUID); loggerExternal.exiting(getClassNameLogging(), "getUniqueIdentifier", value); return value; } @Override public String getUniqueIdentifier(String columnLabel) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getUniqueIdentifier", columnLabel); checkClosed(); String value = (String) getValue(findColumn(columnLabel), JDBCType.GUID); loggerExternal.exiting(getClassNameLogging(), "getUniqueIdentifier", value); return value; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } LocalDateTime getLocalDateTime(int columnIndex) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getLocalDateTime", columnIndex); checkClosed(); LocalDateTime value = (LocalDateTime) getValue(columnIndex, JDBCType.LOCALDATETIME); loggerExternal.exiting(getClassNameLogging(), "getLocalDateTime", value); return value; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override public microsoft.sql.DateTimeOffset getDateTimeOffset(int columnIndex) throws SQLServerException { 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; } @Override public microsoft.sql.DateTimeOffset getDateTimeOffset(String columnName) throws SQLServerException { 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 */ @Deprecated(since = "6.5.4") @Override public java.io.InputStream getUnicodeStream(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getUnicodeStream", columnIndex); SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } /** * @deprecated */ @Deprecated(since = "6.5.4") @Override public java.io.InputStream getUnicodeStream(String columnName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getUnicodeStream", columnName); SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override public Object getObject(int i, java.util.Map> map) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getObject", new Object[] {i, map}); SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override public Ref getRef(int i) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getRef"); SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override 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); configureLobs((SQLServerLob) value); return value; } @Override 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); configureLobs((SQLServerLob) value); return value; } @Override 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); configureLobs((SQLServerLob) value); return value; } @Override 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); configureLobs((SQLServerLob) value); return value; } @Override public NClob getNClob(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNClob", columnIndex); checkClosed(); NClob value = (NClob) getValue(columnIndex, JDBCType.NCLOB); loggerExternal.exiting(getClassNameLogging(), "getNClob", value); configureLobs((SQLServerLob) value); return value; } @Override public NClob getNClob(String columnLabel) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNClob", columnLabel); checkClosed(); NClob value = (NClob) getValue(findColumn(columnLabel), JDBCType.NCLOB); loggerExternal.exiting(getClassNameLogging(), "getNClob", value); configureLobs((SQLServerLob) value); return value; } @Override public Array getArray(int i) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override public Object getObject(String colName, java.util.Map> map) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override public Ref getRef(String colName) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override public Array getArray(String colName) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override public String getCursorName() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getCursorName"); SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_positionedUpdatesNotSupported"), null, false); loggerExternal.exiting(getClassNameLogging(), "getCursorName", null); return null; } @Override public java.io.Reader getCharacterStream(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getCharacterStream", columnIndex); checkClosed(); Reader value = (Reader) getStream(columnIndex, StreamType.CHARACTER); loggerExternal.exiting(getClassNameLogging(), "getCharacterStream", value); return value; } @Override public java.io.Reader getCharacterStream(String columnName) throws SQLException { checkClosed(); loggerExternal.entering(getClassNameLogging(), "getCharacterStream", columnName); Reader value = (Reader) getStream(findColumn(columnName), StreamType.CHARACTER); loggerExternal.exiting(getClassNameLogging(), "getCharacterStream", value); return value; } @Override public Reader getNCharacterStream(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", columnIndex); checkClosed(); Reader value = (Reader) getStream(columnIndex, StreamType.NCHARACTER); loggerExternal.exiting(getClassNameLogging(), "getNCharacterStream", value); return value; } @Override public Reader getNCharacterStream(String columnLabel) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", columnLabel); checkClosed(); Reader value = (Reader) getStream(findColumn(columnLabel), StreamType.NCHARACTER); loggerExternal.exiting(getClassNameLogging(), "getNCharacterStream", value); return value; } @Override public BigDecimal getBigDecimal(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getBigDecimal", columnIndex); checkClosed(); BigDecimal value = (BigDecimal) getValue(columnIndex, JDBCType.DECIMAL); loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value); return value; } @Override public BigDecimal getBigDecimal(String columnName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getBigDecimal", columnName); checkClosed(); BigDecimal value = (BigDecimal) getValue(findColumn(columnName), JDBCType.DECIMAL); loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value); return value; } @Override 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; } @Override 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; } @Override 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; } @Override 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; } @Override public RowId getRowId(int columnIndex) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override public RowId getRowId(String columnLabel) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override public SQLXML getSQLXML(int columnIndex) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getSQLXML", columnIndex); SQLXML xml = getSQLXMLInternal(columnIndex); loggerExternal.exiting(getClassNameLogging(), "getSQLXML", xml); return xml; } @Override public SQLXML getSQLXML(String columnLabel) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getSQLXML", columnLabel); SQLXML xml = getSQLXMLInternal(findColumn(columnLabel)); loggerExternal.exiting(getClassNameLogging(), "getSQLXML", xml); return xml; } @Override 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; } @Override 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; } @Override 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, stmt)); } /* ---------------- 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"), SQLSTATE_INVALID_DESCRIPTOR_INDEX, 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); } @Override 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"); } @Override public void updateBoolean(int index, boolean x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateBoolean", new Object[] {index, x}); checkClosed(); updateValue(index, JDBCType.BIT, x, JavaType.BOOLEAN, false); loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } @Override 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, x, JavaType.BOOLEAN, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } @Override public void updateByte(int index, byte x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateByte", new Object[] {index, x}); checkClosed(); updateValue(index, JDBCType.TINYINT, x, JavaType.BYTE, false); loggerExternal.exiting(getClassNameLogging(), "updateByte"); } @Override 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, x, JavaType.BYTE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateByte"); } @Override public void updateShort(int index, short x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateShort", new Object[] {index, x}); checkClosed(); updateValue(index, JDBCType.SMALLINT, x, JavaType.SHORT, false); loggerExternal.exiting(getClassNameLogging(), "updateShort"); } @Override 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, x, JavaType.SHORT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateShort"); } @Override public void updateInt(int index, int x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateInt", new Object[] {index, x}); checkClosed(); updateValue(index, JDBCType.INTEGER, x, JavaType.INTEGER, false); loggerExternal.exiting(getClassNameLogging(), "updateInt"); } @Override 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, x, JavaType.INTEGER, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateInt"); } @Override public void updateLong(int index, long x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateLong", new Object[] {index, x}); checkClosed(); updateValue(index, JDBCType.BIGINT, x, JavaType.LONG, false); loggerExternal.exiting(getClassNameLogging(), "updateLong"); } @Override 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, x, JavaType.LONG, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateLong"); } @Override public void updateFloat(int index, float x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateFloat", new Object[] {index, x}); checkClosed(); updateValue(index, JDBCType.REAL, x, JavaType.FLOAT, false); loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } @Override 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, x, JavaType.FLOAT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } @Override public void updateDouble(int index, double x) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateDouble", new Object[] {index, x}); checkClosed(); updateValue(index, JDBCType.DOUBLE, x, JavaType.DOUBLE, false); loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } @Override 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, x, JavaType.DOUBLE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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}); checkClosed(); updateValue(columnIndex, JDBCType.NVARCHAR, nString, JavaType.STRING, false); loggerExternal.exiting(getClassNameLogging(), "updateNString"); } @Override public void updateNString(int columnIndex, String nString, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateNString", new Object[] {columnIndex, nString, forceEncrypt}); checkClosed(); updateValue(columnIndex, JDBCType.NVARCHAR, nString, JavaType.STRING, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateNString"); } @Override 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}); checkClosed(); updateValue(findColumn(columnLabel), JDBCType.NVARCHAR, nString, JavaType.STRING, false); loggerExternal.exiting(getClassNameLogging(), "updateNString"); } @Override public void updateNString(String columnLabel, String nString, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateNString", new Object[] {columnLabel, nString, forceEncrypt}); checkClosed(); updateValue(findColumn(columnLabel), JDBCType.NVARCHAR, nString, JavaType.STRING, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateNString"); } @Override public void updateBytes(int index, byte[] x) throws SQLException { 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override public void updateDateTimeOffset(int index, microsoft.sql.DateTimeOffset x) throws SQLServerException { 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"); } @Override public void updateDateTimeOffset(int index, microsoft.sql.DateTimeOffset x, Integer scale) throws SQLServerException { 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"); } @Override public void updateDateTimeOffset(int index, microsoft.sql.DateTimeOffset x, Integer scale, boolean forceEncrypt) throws SQLServerException { 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"); } @Override public void updateUniqueIdentifier(int index, String x) throws SQLServerException { 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"); } @Override public void updateUniqueIdentifier(int index, String x, boolean forceEncrypt) throws SQLServerException { 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"); } @Override public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { 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"); } @Override public void updateAsciiStream(int index, 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"); } @Override public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { loggerExternal.entering(getClassNameLogging(), "updateAsciiStream", new Object[] {columnIndex, x, length}); checkClosed(); updateStream(columnIndex, StreamType.ASCII, x, JavaType.INPUTSTREAM, length); loggerExternal.exiting(getClassNameLogging(), "updateAsciiStream"); } @Override public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { 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"); } @Override public void updateAsciiStream(java.lang.String columnName, 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"); } @Override public void updateAsciiStream(String columnName, InputStream streamValue, long length) throws SQLException { 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"); } @Override public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { 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"); } @Override 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"); } @Override public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { 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"); } @Override public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { 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"); } @Override 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"); } @Override public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { 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"); } @Override public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { 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"); } @Override 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"); } @Override public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { 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"); } @Override public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { 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"); } @Override 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"); } @Override public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { 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"); } @Override public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { 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"); } @Override public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { 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"); } @Override public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { 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"); } @Override public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { 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"); } @Override 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"); } @Override 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, scale, null, null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override 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, scale, null, precision, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override 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, scale, null, precision, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } 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); } } @Override 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"); } @Override 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, x, JavaType.BOOLEAN, false); loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } @Override 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, x, JavaType.BOOLEAN, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateBoolean"); } @Override 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"); } @Override 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"); } @Override 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, x, JavaType.SHORT, false); loggerExternal.exiting(getClassNameLogging(), "updateShort"); } @Override 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, x, JavaType.SHORT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateShort"); } @Override 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, x, JavaType.INTEGER, false); loggerExternal.exiting(getClassNameLogging(), "updateInt"); } @Override 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, x, JavaType.INTEGER, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateInt"); } @Override 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, x, JavaType.LONG, false); loggerExternal.exiting(getClassNameLogging(), "updateLong"); } @Override 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, x, JavaType.LONG, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateLong"); } @Override 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, x, JavaType.FLOAT, false); loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } @Override 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, x, JavaType.FLOAT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateFloat"); } @Override 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, x, JavaType.DOUBLE, false); loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } @Override 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, x, JavaType.DOUBLE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateDouble"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override 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"); } @Override public void updateDateTimeOffset(String columnName, microsoft.sql.DateTimeOffset x) throws SQLServerException { 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"); } @Override public void updateDateTimeOffset(String columnName, microsoft.sql.DateTimeOffset x, int scale) throws SQLServerException { 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"); } @Override public void updateDateTimeOffset(String columnName, microsoft.sql.DateTimeOffset x, int scale, boolean forceEncrypt) throws SQLServerException { 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"); } @Override public void updateUniqueIdentifier(String columnName, String x) throws SQLServerException { 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"); } @Override public void updateUniqueIdentifier(String columnName, String x, boolean forceEncrypt) throws SQLServerException { 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"); } @Override 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, scale, null, null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override 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, scale, null, precision, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override 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, scale, null, precision, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override 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"); } @Override public void updateRowId(int columnIndex, RowId x) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); } @Override public void updateRowId(String columnLabel, RowId x) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); } @Override public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateSQLXML", new Object[] {columnIndex, xmlObject}); updateSQLXMLInternal(columnIndex, xmlObject); loggerExternal.exiting(getClassNameLogging(), "updateSQLXML"); } @Override 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}); updateSQLXMLInternal(findColumn(columnLabel), x); loggerExternal.exiting(getClassNameLogging(), "updateSQLXML"); } @Override public int getHoldability() throws SQLException { loggerExternal.entering(getClassNameLogging(), "getHoldability"); 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 ------------------------- */ @Override public void insertRow() throws SQLException { loggerExternal.entering(getClassNameLogging(), "insertRow"); if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) { loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().toString()); } final class InsertRowRPC extends TDSCommand { /** * Always update serialVersionUID when prompted. */ private static final long serialVersionUID = 1L; final String tableName; InsertRowRPC(String tableName) { super("InsertRowRPC", 0, 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 (Column column : columns) { if (column.hasUpdates()) { tableColumn = column; break; } if (null == tableColumn && column.isUpdatable()) tableColumn = column; } 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.sendEnclavePackage(null, null); tdsWriter.writeRPCInt(null, serverCursorId, false); tdsWriter.writeRPCInt(null, (int) TDS.SP_CURSOR_OP_INSERT, false); tdsWriter.writeRPCInt(null, fetchBufferGetRow(), false); if (hasUpdatedColumns()) { tdsWriter.writeRPCStringUnicode(tableName); for (Column column : columns) column.sendByRPC(tdsWriter, stmt); } else { tdsWriter.writeRPCStringUnicode(""); tdsWriter.writeRPCStringUnicode("INSERT INTO " + tableName + " DEFAULT VALUES"); } TDSParser.parse(command.startResponse(), command.getLogContext()); } @Override public void updateRow() throws SQLException { loggerExternal.entering(getClassNameLogging(), "updateRow"); if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) { loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().toString()); } final class UpdateRowRPC extends TDSCommand { /** * Always update serialVersionUID when prompted. */ private static final long serialVersionUID = 1L; UpdateRowRPC() { super("UpdateRowRPC", 0, 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.sendEnclavePackage(null, null); tdsWriter.writeRPCInt(null, serverCursorId, false); tdsWriter.writeRPCInt(null, TDS.SP_CURSOR_OP_UPDATE | TDS.SP_CURSOR_OP_SETPOSITION, false); tdsWriter.writeRPCInt(null, fetchBufferGetRow(), false); tdsWriter.writeRPCStringUnicode(getUpdatedColumnTableName()); assert hasUpdatedColumns(); for (Column column : columns) column.sendByRPC(tdsWriter, stmt); TDSParser.parse(command.startResponse(), command.getLogContext()); } /** Determines whether there are updated columns in this result set. */ final boolean hasUpdatedColumns() { for (Column column : columns) if (column.hasUpdates()) return true; return false; } private String getUpdatedColumnTableName() throws SQLServerException { String columnTableName = ""; for (Column column : columns) { if (column.hasUpdates() && columnTableName.isEmpty()) { columnTableName = column.getTableName().asEscapedString(); } else if (column.hasUpdates() && !columnTableName.equals(column.getTableName().asEscapedString())) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_AmbiguousRowUpdate")); Object[] msgArgs = {columnTableName, column.getTableName().asEscapedString()}; SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), null, false); } } return columnTableName; } @Override public void deleteRow() throws SQLException { loggerExternal.entering(getClassNameLogging(), "deleteRow"); if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) { loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().toString()); } final class DeleteRowRPC extends TDSCommand { /** * Always update serialVersionUID when prompted. */ private static final long serialVersionUID = 1L; DeleteRowRPC() { super("DeleteRowRPC", 0, 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.sendEnclavePackage(null, null); tdsWriter.writeRPCInt(null, serverCursorId, false); tdsWriter.writeRPCInt(null, TDS.SP_CURSOR_OP_DELETE | TDS.SP_CURSOR_OP_SETPOSITION, false); tdsWriter.writeRPCInt(null, fetchBufferGetRow(), false); tdsWriter.writeRPCStringUnicode(""); TDSParser.parse(command.startResponse(), command.getLogContext()); } @Override public void refreshRow() throws SQLException { loggerExternal.entering(getClassNameLogging(), "refreshRow"); if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) { loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().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(); } @Override 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"); } @Override 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"); } @Override 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"); } @Override public java.sql.Statement getStatement() throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getStatement"); checkClosed(); loggerExternal.exiting(getClassNameLogging(), "getStatement", stmt); return stmt; } /* JDBC 3.0 */ @Override 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"); } @Override public void updateClob(int columnIndex, Reader reader) throws SQLException { 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"); } @Override public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { 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"); } @Override 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"); } @Override public void updateClob(String columnLabel, Reader reader) throws SQLException { 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"); } @Override public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { 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"); } @Override public void updateNClob(int columnIndex, NClob nClob) throws SQLException { 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"); } @Override public void updateNClob(int columnIndex, Reader reader) throws SQLException { 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"); } @Override public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { 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"); } @Override public void updateNClob(String columnLabel, NClob nClob) throws SQLException { 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"); } @Override public void updateNClob(String columnLabel, Reader reader) throws SQLException { 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"); } @Override public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { 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"); } @Override 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"); } @Override public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { 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"); } @Override public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { 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"); } @Override 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"); } @Override public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { 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"); } @Override public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { 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"); } @Override public void updateArray(int columnIndex, Array x) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); } @Override public void updateArray(java.lang.String columnName, Array x) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); } @Override public void updateRef(int columnIndex, Ref x) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); } @Override public void updateRef(java.lang.String columnName, Ref x) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); } @Override public java.net.URL getURL(int columnIndex) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); return null; } @Override public java.net.URL getURL(String sColumn) throws SQLException { SQLServerException.throwNotSupportedException(stmt.connection, stmt); 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. @Override boolean onColMetaData(TDSReader tdsReader) throws SQLServerException { (new StreamColumns(Util.shouldHonorAEForRead(stmt.stmtColumnEncriptionSetting, stmt.connection))) .setFromTDS(tdsReader); return true; } @Override 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; } @Override 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; } @Override boolean onDone(TDSReader tdsReader) throws SQLServerException { ensureStartMark(); StreamDone doneToken = new StreamDone(); doneToken.setFromTDS(tdsReader); if (doneToken.isFinal()) { stmt.connection.getSessionRecovery().decrementUnprocessedResponseCount(); } if (doneToken.isFinal() && doneToken.isError()) { short status = tdsReader.peekStatusFlag(); SQLServerError databaseError = getDatabaseError(); MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_serverError")); Object[] msgArgs = {status, (databaseError != null) ? databaseError.getErrorMessage() : ""}; if (null != databaseError) { SQLServerException.makeFromDatabaseError(stmt.connection, null, form.format(msgArgs), databaseError, false); } else { SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), null, false); } } // 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; } @Override 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; } @Override void onEOF(TDSReader tdsReader) throws SQLServerException { super.onEOF(tdsReader); done = true; } @Override boolean onDataClassification(TDSReader tdsReader) throws SQLServerException { if (tdsReader.getServerSupportsDataClassification()) { tdsReader.trySetSensitivityClassification(new StreamColumns( Util.shouldHonorAEForRead(stmt.stmtColumnEncriptionSetting, stmt.connection)) .processDataClassification(tdsReader)); } return 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(super.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 (null != fetchBufferTokenHandler.getDatabaseError()) { SQLServerException.makeFromDatabaseError(stmt.connection, null, fetchBufferTokenHandler.getDatabaseError().getErrorMessage(), fetchBufferTokenHandler.getDatabaseError(), false); } return fetchBufferCurrentRowType; } } /** * Cursor Fetch Command */ private final class CursorFetchCommand extends TDSCommand { /** * Always update serialVersionUID when prompted. */ private static final long serialVersionUID = 1L; 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, stmt.cancelQueryTimeoutSeconds); 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.sendEnclavePackage(null, null); tdsWriter.writeRPCInt(null, serverCursorId, false); tdsWriter.writeRPCInt(null, fetchType, false); tdsWriter.writeRPCInt(null, startRow, false); tdsWriter.writeRPCInt(null, 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; } @Override 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 != null && 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. if (null != scrollWindow) scrollWindow.reset(); } } /* * Checks for any LOBs which need to be available after the RS is closed, and loads their contents from stream into * memory. Closed LOBs will not be populated. */ private void fillLOBs() { if (null != activeLOB) { try { activeLOB.fillFromStream(); } catch (SQLException e) { if (logger.isLoggable(java.util.logging.Level.FINER)) { logger.finer(toString() + "Filling Lobs before closing: " + e.getMessage()); } } finally { activeLOB = null; } } } /** * 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() { // fills blobs before discarding anything fillLOBs(); // 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 { /** * Always update serialVersionUID when prompted. */ private static final long serialVersionUID = 1L; 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.sendEnclavePackage(null, null); tdsWriter.writeRPCInt(null, 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); } } @Override public void updateObject(int index, Object obj, SQLType targetSqlType) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj, targetSqlType}); checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types updateObject(index, obj, null, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override public void updateObject(int index, Object obj, SQLType targetSqlType, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj, targetSqlType, scale}); checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types updateObject(index, obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override public void updateObject(int index, Object obj, SQLType targetSqlType, int scale, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {index, obj, targetSqlType, scale, forceEncrypt}); checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types updateObject(index, obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override public void updateObject(String columnName, Object obj, SQLType targetSqlType, int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, obj, targetSqlType, scale}); checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types updateObject(findColumn(columnName), obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override public void updateObject(String columnName, Object obj, SQLType targetSqlType, int scale, boolean forceEncrypt) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, obj, targetSqlType, scale, forceEncrypt}); checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types updateObject(findColumn(columnName), obj, scale, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } @Override public void updateObject(String columnName, Object obj, SQLType targetSqlType) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "updateObject", new Object[] {columnName, obj, targetSqlType}); checkClosed(); // getVendorTypeNumber() returns the same constant integer values as in java.sql.Types updateObject(findColumn(columnName), obj, null, JDBCType.of(targetSqlType.getVendorTypeNumber()), null, false); loggerExternal.exiting(getClassNameLogging(), "updateObject"); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy