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

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

Go to download

Microsoft JDBC Driver for SQL Server. The Azure Key Vault feature in Microsoft JDBC Driver for SQL Server depends on Azure SDK for JAVA and Azure Active Directory Library For Java.

There is a newer version: 12.7.0.jre11-preview
Show 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.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverPropertyInfo;
import java.sql.ResultSet;
import java.sql.RowIdLifetime;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.EnumMap;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

/**
 * SQLServerDatabaseMetaData provides JDBC database meta data.
 *
 * The API javadoc for JDBC API methods that this class implements are not repeated here. Please see Sun's JDBC API interfaces javadoc for those
 * details.
 */
public final class SQLServerDatabaseMetaData implements java.sql.DatabaseMetaData {
    private SQLServerConnection connection;

    static final String urlprefix = "jdbc:sqlserver://";

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

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

    static private final AtomicInteger baseID = new AtomicInteger(0);	// Unique id generator for each instance (used for logging).

    final private String traceID;

    // varbinary(max) https://msdn.microsoft.com/en-us/library/ms143432.aspx
    static final int MAXLOBSIZE = 2147483647;
    // uniqueidentifier https://msdn.microsoft.com/en-us/library/ms187942.aspx
    static final int uniqueidentifierSize = 36;

    enum CallableHandles
    {
        SP_COLUMNS              ("{ call sp_columns(?, ?, ?, ?, ?) }",              "{ call sp_columns_100(?, ?, ?, ?, ?, ?) }"),
        SP_COLUMN_PRIVILEGES    ("{ call sp_column_privileges(?, ?, ?, ?)}",        "{ call sp_column_privileges(?, ?, ?, ?)}"), 
        SP_TABLES               ("{ call sp_tables(?, ?, ?, ?) }",                  "{ call sp_tables(?, ?, ?, ?) }"),
        SP_SPECIAL_COLUMNS      ("{ call sp_special_columns (?, ?, ?, ?, ?, ?, ?)}","{ call sp_special_columns_100 (?, ?, ?, ?, ?, ?, ?)}"),
        SP_FKEYS                ("{ call sp_fkeys (?, ?, ?, ? , ? ,?)}",            "{ call sp_fkeys (?, ?, ?, ? , ? ,?)}"),
        SP_STATISTICS           ("{ call sp_statistics(?,?,?,?,?, ?) }",            "{ call sp_statistics_100(?,?,?,?,?, ?) }"),
        SP_SPROC_COLUMNS        ("{ call sp_sproc_columns(?, ?, ?,?,?) }",          "{ call sp_sproc_columns_100(?, ?, ?,?,?) }"), 
        SP_STORED_PROCEDURES    ("{call sp_stored_procedures(?, ?, ?) }",           "{call sp_stored_procedures(?, ?, ?) }"),
        SP_TABLE_PRIVILEGES     ("{call sp_table_privileges(?,?,?) }",              "{call sp_table_privileges(?,?,?) }"), 
        SP_PKEYS                ("{ call sp_pkeys (?, ?, ?)}",                      "{ call sp_pkeys (?, ?, ?)}");
        // stored procs before Katmai ie SS10
        private final String preKatProc;
        // procs on or after katmai
        private final String katProc;

        private CallableHandles(String name,
                String katName) {
            this.preKatProc = name;
            this.katProc = katName;
        }

        CallableStatement prepare(SQLServerConnection conn) throws SQLServerException {
            return conn.prepareCall(conn.isKatmaiOrLater() ? katProc : preKatProc);
        }
    }

    final class HandleAssociation {
        final String databaseName;
        final CallableStatement stmt;

        HandleAssociation(String databaseName,
                CallableStatement stmt) {
            this.databaseName = databaseName;
            this.stmt = stmt;
        }

        final void close() throws SQLServerException {
            ((SQLServerCallableStatement) stmt).close();
        }
    }

    EnumMap handleMap = new EnumMap(CallableHandles.class);

    // Returns unique id for each instance.
    private static int nextInstanceID() {
        return baseID.incrementAndGet();
    }

    /**
     * This is a helper function to provide an ID string suitable for tracing.
     * 
     * @return traceID string
     */
    final public String toString() {
        return traceID;
    }

    /**
     * Create new database meta data
     * 
     * @param con
     *            the connection
     */
    /* L0 */ public SQLServerDatabaseMetaData(SQLServerConnection con) {
        traceID = " SQLServerDatabaseMetaData:" + nextInstanceID();
        connection = con;
        if (logger.isLoggable(java.util.logging.Level.FINE)) {
            logger.fine(toString() + " created by (" + connection.toString() + ")");
        }
    }

    public boolean isWrapperFor(Class iface) throws SQLException {
        boolean f = iface.isInstance(this);
        return f;
    }

    public  T unwrap(Class iface) throws SQLException {
        T t;
        try {
            t = iface.cast(this);
        }
        catch (ClassCastException e) {
            throw new SQLServerException(e.getMessage(), e);
        }
        return t;
    }

    private void checkClosed() throws SQLServerException {
        if (connection.isClosed()) {
            SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_connectionIsClosed"),
                    SQLServerException.EXCEPTION_XOPEN_CONNECTION_DOES_NOT_EXIST, false);
        }
    }

    private final static String ASC_OR_DESC = "ASC_OR_DESC";
    private final static String ATTR_NAME = "ATTR_NAME";
    private final static String ATTR_TYPE_NAME = "ATTR_TYPE_NAME";
    private final static String ATTR_SIZE = "ATTR_SIZE";
    private final static String ATTR_DEF = "ATTR_DEF";
    private final static String BASE_TYPE = "BASE_TYPE";
    private final static String BUFFER_LENGTH = "BUFFER_LENGTH";
    private final static String CARDINALITY = "CARDINALITY";
    private final static String CHAR_OCTET_LENGTH = "CHAR_OCTET_LENGTH";
    private final static String CLASS_NAME = "CLASS_NAME";
    private final static String COLUMN_DEF = "COLUMN_DEF";
    private final static String COLUMN_NAME = "COLUMN_NAME";
    private final static String COLUMN_SIZE = "COLUMN_SIZE";
    private final static String COLUMN_TYPE = "COLUMN_TYPE";
    private final static String DATA_TYPE = "DATA_TYPE";
    private final static String DECIMAL_DIGITS = "DECIMAL_DIGITS";
    private final static String DEFERRABILITY = "DEFERRABILITY";
    private final static String DELETE_RULE = "DELETE_RULE";
    private final static String FILTER_CONDITION = "FILTER_CONDITION";
    private final static String FK_NAME = "FK_NAME";
    private final static String FKCOLUMN_NAME = "FKCOLUMN_NAME";
    private final static String FKTABLE_CAT = "FKTABLE_CAT";
    private final static String FKTABLE_NAME = "FKTABLE_NAME";
    private final static String FKTABLE_SCHEM = "FKTABLE_SCHEM";
    private final static String GRANTEE = "GRANTEE";
    private final static String GRANTOR = "GRANTOR";
    private final static String INDEX_NAME = "INDEX_NAME";
    private final static String INDEX_QUALIFIER = "INDEX_QUALIFIER";
    private final static String IS_GRANTABLE = "IS_GRANTABLE";
    private final static String IS_NULLABLE = "IS_NULLABLE";
    private final static String KEY_SEQ = "KEY_SEQ";
    private final static String LENGTH = "LENGTH";
    private final static String NON_UNIQUE = "NON_UNIQUE";
    private final static String NULLABLE = "NULLABLE";
    private final static String NUM_INPUT_PARAMS = "NUM_INPUT_PARAMS";
    private final static String NUM_OUTPUT_PARAMS = "NUM_OUTPUT_PARAMS";
    private final static String NUM_PREC_RADIX = "NUM_PREC_RADIX";
    private final static String NUM_RESULT_SETS = "NUM_RESULT_SETS";
    private final static String ORDINAL_POSITION = "ORDINAL_POSITION";
    private final static String PAGES = "PAGES";
    private final static String PK_NAME = "PK_NAME";
    private final static String PKCOLUMN_NAME = "PKCOLUMN_NAME";
    private final static String PKTABLE_CAT = "PKTABLE_CAT";
    private final static String PKTABLE_NAME = "PKTABLE_NAME";
    private final static String PKTABLE_SCHEM = "PKTABLE_SCHEM";
    private final static String PRECISION = "PRECISION";
    private final static String PRIVILEGE = "PRIVILEGE";
    private final static String PROCEDURE_CAT = "PROCEDURE_CAT";
    private final static String PROCEDURE_NAME = "PROCEDURE_NAME";
    private final static String PROCEDURE_SCHEM = "PROCEDURE_SCHEM";
    private final static String PROCEDURE_TYPE = "PROCEDURE_TYPE";
    private final static String PSEUDO_COLUMN = "PSEUDO_COLUMN";
    private final static String RADIX = "RADIX";
    private final static String REMARKS = "REMARKS";
    private final static String SCALE = "SCALE";
    private final static String SCOPE = "SCOPE";
    private final static String SCOPE_CATALOG = "SCOPE_CATALOG";
    private final static String SCOPE_SCHEMA = "SCOPE_SCHEMA";
    private final static String SCOPE_TABLE = "SCOPE_TABLE";
    private final static String SOURCE_DATA_TYPE = "SOURCE_DATA_TYPE";
    private final static String SQL_DATA_TYPE = "SQL_DATA_TYPE";
    private final static String SQL_DATETIME_SUB = "SQL_DATETIME_SUB";
    private final static String SS_DATA_TYPE = "SS_DATA_TYPE";
    private final static String SUPERTABLE_NAME = "SUPERTABLE_NAME";
    private final static String SUPERTYPE_CAT = "SUPERTYPE_CAT";
    private final static String SUPERTYPE_NAME = "SUPERTYPE_NAME";
    private final static String SUPERTYPE_SCHEM = "SUPERTYPE_SCHEM";
    private final static String TABLE_CAT = "TABLE_CAT";
    private final static String TABLE_NAME = "TABLE_NAME";
    private final static String TABLE_SCHEM = "TABLE_SCHEM";
    private final static String TABLE_TYPE = "TABLE_TYPE";
    private final static String TYPE = "TYPE";
    private final static String TYPE_CAT = "TYPE_CAT";
    private final static String TYPE_NAME = "TYPE_NAME";
    private final static String TYPE_SCHEM = "TYPE_SCHEM";
    private final static String UPDATE_RULE = "UPDATE_RULE";
    private final static String FUNCTION_CAT = "FUNCTION_CAT";
    private final static String FUNCTION_NAME = "FUNCTION_NAME";
    private final static String FUNCTION_SCHEM = "FUNCTION_SCHEM";
    private final static String FUNCTION_TYPE = "FUNCTION_TYPE";
    private final static String SS_IS_SPARSE = "SS_IS_SPARSE";
    private final static String SS_IS_COLUMN_SET = "SS_IS_COLUMN_SET";
    private final static String SS_IS_COMPUTED = "SS_IS_COMPUTED";
    private final static String IS_AUTOINCREMENT = "IS_AUTOINCREMENT";

    /**
     * Make a simple query execute and return the result from it. This is to be used only for internal queries without any user input.
     * 
     * @param catalog
     *            catalog the query to be made in
     * @param query
     *            to execute
     * @return Resultset from the execution
     */
    private SQLServerResultSet getResultSetFromInternalQueries(String catalog,
            String query) throws SQLServerException {
        checkClosed();
        String orgCat = null;
        orgCat = switchCatalogs(catalog);
        SQLServerResultSet rs = null;
        try {
            rs = ((SQLServerStatement) connection.createStatement()).executeQueryInternal(query);

        }
        finally {
            if (null != orgCat) {
                connection.setCatalog(orgCat);
            }
        }
        return rs;
    }

    /*
     * Note we pool the handles per object.
     */
    private CallableStatement getCallableStatementHandle(CallableHandles request,
            String catalog) throws SQLServerException {
        CallableStatement CS = null;
        HandleAssociation hassoc = handleMap.get(request);
        if (null == hassoc || null == hassoc.databaseName || !hassoc.databaseName.equals(catalog)) {
            CS = request.prepare(connection);
            hassoc = new HandleAssociation(catalog, CS);
            HandleAssociation previous = handleMap.put(request, hassoc);
            if (null != previous) {
                previous.close();
            }
        }
        return hassoc.stmt;
    }

    /**
     * Make the stored procedure call and return the result from it.
     * 
     * @param catalog
     *            catalog the query to be made in
     * @param procedure
     *            to execute
     * @param arguments
     *            for the stored procedure
     * @return Resultset from the execution
     */
    private SQLServerResultSet getResultSetFromStoredProc(String catalog,
            CallableHandles procedure,
            String[] arguments) throws SQLServerException {
        checkClosed();
        assert null != arguments;
        String orgCat = null;
        orgCat = switchCatalogs(catalog);
        SQLServerResultSet rs = null;
        try {
            SQLServerCallableStatement call = (SQLServerCallableStatement) getCallableStatementHandle(procedure, catalog);

            for (int i = 1; i <= arguments.length; i++) {
                // note individual arguments can be null.
                call.setString(i, arguments[i - 1]);
            }
            rs = (SQLServerResultSet) call.executeQueryInternal();
        }
        finally {
            if (null != orgCat) {
                connection.setCatalog(orgCat);
            }
        }
        return rs;
    }

    private SQLServerResultSet getResultSetWithProvidedColumnNames(String catalog,
            CallableHandles procedure,
            String[] arguments,
            String[] columnNames) throws SQLServerException {
        // Execute the query
        SQLServerResultSet rs = getResultSetFromStoredProc(catalog, procedure, arguments);

        // Rename the columns
        for (int i = 0; i < columnNames.length; i++)
            rs.setColumnName(1 + i, columnNames[i]);
        return rs;
    }

    /**
     * Switch database catalogs.
     * 
     * @param catalog
     *            the new catalog
     * @throws SQLServerException
     * @return the old catalog
     */
    /* L0 */ private String switchCatalogs(String catalog) throws SQLServerException {
        if (catalog == null)
            return null;
        String sCurr = null;
        sCurr = connection.getCatalog().trim();
        String sNew = catalog.trim();
        if (sCurr.equals(sNew))
            return null;
        connection.setCatalog(sNew);
        if (sCurr == null || sCurr.length() == 0)
            return null;
        return sCurr;
    }

    /* -------------- JDBC Interface API starts here ---------------- */

    /* L0 */ public boolean allProceduresAreCallable() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean allTablesAreSelectable() throws SQLServerException {
        checkClosed();
        return true;
    }

    public boolean autoCommitFailureClosesAllResultSets() throws SQLException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean dataDefinitionCausesTransactionCommit() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean dataDefinitionIgnoredInTransactions() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean doesMaxRowSizeIncludeBlobs() throws SQLServerException {
        checkClosed();
        return false;
    }

    public boolean generatedKeyAlwaysReturned() throws SQLException {
        checkClosed();

        // driver supports retrieving generated keys
        return true;
    }

    public long getMaxLogicalLobSize() throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC42();
        checkClosed();

        return MAXLOBSIZE;
    }

    public boolean supportsRefCursors() throws SQLException {
        DriverJDBCVersion.checkSupportsJDBC42();
        checkClosed();

        return false;
    }

    /* L0 */ public java.sql.ResultSet getCatalogs() throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        // Return the orginal case instead of CAPS.removed Upper().
        String s = "SELECT name AS TABLE_CAT FROM sys.databases order by name"; // Need to match case of connection.getCatalog
        return getResultSetFromInternalQueries(null, s);
    }

    /* L0 */ public String getCatalogSeparator() throws SQLServerException {
        checkClosed();
        return ".";
    }

    /* L0 */ public String getCatalogTerm() throws SQLServerException {
        checkClosed();
        return "database";
    }

    private final static String[] getColumnPrivilegesColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME,
            /* 5 */ GRANTOR, /* 6 */ GRANTEE, /* 7 */ PRIVILEGE, /* 8 */ IS_GRANTABLE};

    /* L0 */ public java.sql.ResultSet getColumnPrivileges(String catalog,
            String schema,
            String table,
            String col) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        // column_privileges supports columns being escaped.
        col = EscapeIDName(col);
        /*
         * sp_column_privileges [ @table_name = ] 'table_name' [ , [ @table_owner = ] 'table_owner' ] [ , [ @table_qualifier = ] 'table_qualifier' ] [
         * , [ @column_name = ] 'column' ]
         */

        String[] arguments = new String[4];
        arguments[0] = table;
        arguments[1] = schema;
        arguments[2] = catalog;
        arguments[3] = col;
        return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_COLUMN_PRIVILEGES, arguments, getColumnPrivilegesColumnNames);
    }

    private final static String[] getTablesColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ TABLE_TYPE,
            /* 5 */ REMARKS};

    /* L0 */ public java.sql.ResultSet getTables(String catalog,
            String schema,
            String table,
            String types[]) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();

        // sp_tables supports table name and owner ie schema escaped.
        table = EscapeIDName(table);
        schema = EscapeIDName(schema);
        /*
         * sp_tables [ [ @table_name = ] 'name' ] [ , [ @table_owner = ] 'owner' ] [ , [ @table_qualifier = ] 'qualifier' ] [ , [ @table_type = ]
         * "type" ]
         */

        String[] arguments = new String[4];
        arguments[0] = table;
        arguments[1] = schema;
        arguments[2] = catalog;

        String tableTypes = null;
        if (types != null) {
            tableTypes = "'";
            for (int i = 0; i < types.length; i++) {
                if (i > 0)
                    tableTypes += ",";
                tableTypes += "''" + types[i] + "''";
            }
            tableTypes += "'";
        }
        arguments[3] = tableTypes;
        return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_TABLES, arguments, getTablesColumnNames);
    }

    static final char LEFT_BRACKET = '[';
    static final char RIGHT_BRACKET = ']';
    static final char ESCAPE = '\\';
    static final char PERCENT = '%';
    static final char UNDERSCORE = '_';
    static final char DOUBLE_RIGHT_BRACKET[] = {']', ']'};

    /**
     * Accepts a SQL identifier (such as a column name or table name) and escapes the identifier so sql 92 wild card characters can be escaped
     * properly to be passed to functions like sp_columns or sp_tables. Assumes that the incoming identifier is unescaped.
     * 
     * @inID input identifier to escape.
     * @return the escaped value.
     */
    private static String EscapeIDName(String inID) throws SQLServerException {
        if (null == inID)
            return inID;
        // SQL bracket escaping rules.
        // See Using Wildcard Characters As Literals in SQL BOL
        //
        // 5\% -> '5[%]'
        // \_n -> '[_]n'
        // \[ -> '[ [ ]'
        // \] -> ']'
        // \\ -> \
        // \x -> \x where x is any char other than the ones above.

        char ch;
        // Add 2 extra chars wild guess thinking atleast one escape.
        StringBuilder outID = new StringBuilder(inID.length() + 2);

        for (int i = 0; i < inID.length(); i++) {
            ch = inID.charAt(i);
            if (ESCAPE == ch && (++i < inID.length())) {
                ch = inID.charAt(i);
                switch (ch) {
                    case PERCENT:
                    case UNDERSCORE:
                    case LEFT_BRACKET:
                        outID.append(LEFT_BRACKET);
                        outID.append(ch);
                        outID.append(RIGHT_BRACKET);
                        break;
                    case RIGHT_BRACKET:
                    case ESCAPE:
                        outID.append(ch);
                        break;
                    default:
                        outID.append(ESCAPE);
                        outID.append(ch);
                }

            }
            else {
                // no escape just copy
                outID.append(ch);
            }
        }
        return outID.toString();
    }

    private final static String[] getColumnsColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME,
            /* 5 */ DATA_TYPE, /* 6 */ TYPE_NAME, /* 7 */ COLUMN_SIZE, /* 8 */ BUFFER_LENGTH, /* 9 */ DECIMAL_DIGITS, /* 10 */ NUM_PREC_RADIX,
            /* 11 */ NULLABLE, /* 12 */ REMARKS, /* 13 */ COLUMN_DEF, /* 14 */ SQL_DATA_TYPE, /* 15 */ SQL_DATETIME_SUB, /* 16 */ CHAR_OCTET_LENGTH,
            /* 17 */ ORDINAL_POSITION, /* 18 */ IS_NULLABLE};
    // SQL10 columns not exahustive we only need to set until the one we want to change
    // in this case we want to change SS_IS_IDENTITY 22nd column to IS_AUTOINCREMENT
    // to be inline with JDBC spec
    private final static String[] getColumnsColumnNamesKatmai = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME,
            /* 5 */ DATA_TYPE, /* 6 */ TYPE_NAME, /* 7 */ COLUMN_SIZE, /* 8 */ BUFFER_LENGTH, /* 9 */ DECIMAL_DIGITS, /* 10 */ NUM_PREC_RADIX,
            /* 11 */ NULLABLE, /* 12 */ REMARKS, /* 13 */ COLUMN_DEF, /* 14 */ SQL_DATA_TYPE, /* 15 */ SQL_DATETIME_SUB, /* 16 */ CHAR_OCTET_LENGTH,
            /* 17 */ ORDINAL_POSITION, /* 18 */ IS_NULLABLE, /* 20 */ SS_IS_SPARSE, /* 20 */ SS_IS_COLUMN_SET, /* 21 */ SS_IS_COMPUTED,
            /* 22 */ IS_AUTOINCREMENT};

    /* L0 */ public java.sql.ResultSet getColumns(String catalog,
            String schema,
            String table,
            String col) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();

        // sp_columns supports wild carding schema table and columns
        String column = EscapeIDName(col);
        table = EscapeIDName(table);
        schema = EscapeIDName(schema);

        /*
         * sp_columns [ @table_name = ] object [ , [ @table_owner = ] owner ] [ , [ @table_qualifier = ] qualifier ] [ , [ @column_name = ] column ] [
         * , [ @ODBCVer = ] ODBCVer ]
         */
        String[] arguments;
        if (connection.isKatmaiOrLater())
            arguments = new String[6];
        else
            arguments = new String[5];
        arguments[0] = table;
        arguments[1] = schema;
        arguments[2] = catalog;
        arguments[3] = column;
        if (connection.isKatmaiOrLater()) {
            arguments[4] = "2"; // give information about everything including sparse columns
            arguments[5] = "3"; // odbc version
        }
        else
            arguments[4] = "3"; // odbc version
        SQLServerResultSet rs;
        if (connection.isKatmaiOrLater())
            rs = getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_COLUMNS, arguments, getColumnsColumnNamesKatmai);
        else
            rs = getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_COLUMNS, arguments, getColumnsColumnNames);
        // Hook in a filter on the DATA_TYPE column of the result set we're
        // going to return that converts the ODBC values from sp_columns
        // into JDBC values.
        rs.getColumn(5).setFilter(new DataTypeFilter());

        if (connection.isKatmaiOrLater()) {
            rs.getColumn(22).setFilter(new IntColumnIdentityFilter());
            rs.getColumn(7).setFilter(new ZeroFixupFilter());
            rs.getColumn(8).setFilter(new ZeroFixupFilter());
            rs.getColumn(16).setFilter(new ZeroFixupFilter());
        }
        return rs;
    }

    private final static String[] getFunctionsColumnNames = {/* 1 */ FUNCTION_CAT, /* 2 */ FUNCTION_SCHEM, /* 3 */ FUNCTION_NAME,
            /* 4 */ NUM_INPUT_PARAMS, /* 5 */ NUM_OUTPUT_PARAMS, /* 6 */ NUM_RESULT_SETS, /* 7 */ REMARKS, /* 8 */ FUNCTION_TYPE};

    public java.sql.ResultSet getFunctions(String catalog,
            String schemaPattern,
            String functionNamePattern) throws SQLException {
        checkClosed();

        /*
         * sp_stored_procedures [ [ @sp_name = ] 'name' ] [ , [ @sp_owner = ] 'schema'] [ , [ @sp_qualifier = ] 'qualifier' ] [ , [@fUsePattern = ]
         * 'fUsePattern' ]
         */ // use default ie use pattern matching.
        // catalog cannot be empty in sql server
        if (catalog != null && catalog.length() == 0) {
            MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument"));
            Object[] msgArgs = {"catalog"};
            SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false);
        }

        String[] arguments = new String[3];
        arguments[0] = EscapeIDName(functionNamePattern);
        arguments[1] = EscapeIDName(schemaPattern);
        arguments[2] = catalog;
        return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_STORED_PROCEDURES, arguments, getFunctionsColumnNames);
    }

    private final static String[] getFunctionsColumnsColumnNames = {/* 1 */ FUNCTION_CAT, /* 2 */ FUNCTION_SCHEM, /* 3 */ FUNCTION_NAME,
            /* 4 */ COLUMN_NAME, /* 5 */ COLUMN_TYPE, /* 6 */ DATA_TYPE, /* 7 */ TYPE_NAME, /* 8 */ PRECISION, /* 9 */ LENGTH, /* 10 */ SCALE,
            /* 11 */ RADIX, /* 12 */ NULLABLE, /* 13 */ REMARKS, /* 14 */ COLUMN_DEF, /* 15 */ SQL_DATA_TYPE, /* 16 */ SQL_DATETIME_SUB,
            /* 17 */ CHAR_OCTET_LENGTH, /* 18 */ ORDINAL_POSITION, /* 19 */ IS_NULLABLE};

    public java.sql.ResultSet getFunctionColumns(String catalog,
            String schemaPattern,
            String functionNamePattern,
            String columnNamePattern) throws SQLException {
        checkClosed();
        /*
         * sp_sproc_columns [[@procedure_name =] 'name'] [,[@procedure_owner =] 'owner'] [,[@procedure_qualifier =] 'qualifier'] [,[@column_name =]
         * 'column_name'] [,[@ODBCVer =] 'ODBCVer']
         */

        // catalog cannot be empty in sql server
        if (catalog != null && catalog.length() == 0) {
            MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument"));
            Object[] msgArgs = {"catalog"};
            SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false);
        }

        String[] arguments = new String[5];

        // proc name supports escaping
        arguments[0] = EscapeIDName(functionNamePattern);
        // schema name supports escaping.
        arguments[1] = EscapeIDName(schemaPattern);
        arguments[2] = catalog;
        // col name supports escaping
        arguments[3] = EscapeIDName(columnNamePattern);
        arguments[4] = "3";
        SQLServerResultSet rs = getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_SPROC_COLUMNS, arguments,
                getFunctionsColumnsColumnNames);

        // Hook in a filter on the DATA_TYPE column of the result set we're
        // going to return that converts the ODBC values from sp_columns
        // into JDBC values. Also for the precision
        rs.getColumn(6).setFilter(new DataTypeFilter());

        if (connection.isKatmaiOrLater()) {
            rs.getColumn(8).setFilter(new ZeroFixupFilter());
            rs.getColumn(9).setFilter(new ZeroFixupFilter());
            rs.getColumn(17).setFilter(new ZeroFixupFilter());
        }
        return rs;
    }

    public java.sql.ResultSet getClientInfoProperties() throws SQLException {
        checkClosed();
        return getResultSetFromInternalQueries(null, "SELECT" +
        /* 1 */ " cast(NULL as char(1)) as NAME," +
        /* 2 */ " cast(0 as int) as MAX_LEN," +
        /* 3 */ " cast(NULL as char(1)) as DEFAULT_VALUE," +
        /* 4 */ " cast(NULL as char(1)) as DESCRIPTION " + " where 0 = 1");
    }

    private final static String[] getBestRowIdentifierColumnNames = {/* 1 */ SCOPE, /* 2 */ COLUMN_NAME, /* 3 */ DATA_TYPE, /* 4 */ TYPE_NAME,
            /* 5 */ COLUMN_SIZE, /* 6 */ BUFFER_LENGTH, /* 7 */ DECIMAL_DIGITS, /* 8 */ PSEUDO_COLUMN};

    /* L0 */ public java.sql.ResultSet getBestRowIdentifier(String catalog,
            String schema,
            String table,
            int scope,
            boolean nullable) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        /*
         * sp_special_columns [@table_name =] 'table_name' [,[@table_owner =] 'table_owner'] [,[@qualifier =] 'qualifier'] [,[@col_type =] 'col_type']
         * [,[@scope =] 'scope'] [,[@nullable =] 'nullable'] [,[@ODBCVer =] 'ODBCVer'] ;
         */
        String[] arguments = new String[7];
        arguments[0] = table;
        arguments[1] = schema;
        arguments[2] = catalog;
        arguments[3] = "R"; // coltype
        if (bestRowTemporary == scope)
            arguments[4] = "C"; // Scope is temporary C
        else
            arguments[4] = "T"; // Scope is for the transaction
        if (nullable)
            arguments[5] = "U"; // nullable
        else
            arguments[5] = "O"; // nullable
        arguments[6] = "3"; // Use 3 unless required otherwise
        SQLServerResultSet rs = getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_SPECIAL_COLUMNS, arguments,
                getBestRowIdentifierColumnNames);

        // Hook in a filter on the DATA_TYPE column of the result set we're
        // going to return that converts the ODBC values from sp_columns
        // into JDBC values.
        rs.getColumn(3).setFilter(new DataTypeFilter());
        return rs;
    }

    private final static String[] pkfkColumnNames = {/* 1 */ PKTABLE_CAT, /* 2 */ PKTABLE_SCHEM, /* 3 */ PKTABLE_NAME, /* 4 */ PKCOLUMN_NAME,
            /* 5 */ FKTABLE_CAT, /* 6 */ FKTABLE_SCHEM, /* 7 */ FKTABLE_NAME, /* 8 */ FKCOLUMN_NAME, /* 9 */ KEY_SEQ, /* 10 */ UPDATE_RULE,
            /* 11 */ DELETE_RULE, /* 12 */ FK_NAME, /* 13 */ PK_NAME, /* 14 */ DEFERRABILITY};

    /* L0 */ public java.sql.ResultSet getCrossReference(String cat1,
            String schem1,
            String tab1,
            String cat2,
            String schem2,
            String tab2) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        /*
         * sp_fkeys [ @pktable_name = ] 'pktable_name' [ , [ @pktable_owner = ] 'pktable_owner' ] [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] {
         * , [ @fktable_name = ] 'fktable_name' } [ , [ @fktable_owner = ] 'fktable_owner' ] [ , [ @fktable_qualifier = ] 'fktable_qualifier' ]
         */
        String[] arguments = new String[6];
        arguments[0] = tab1; // pktable_name
        arguments[1] = schem1;
        arguments[2] = cat1;
        arguments[3] = tab2;
        arguments[4] = schem2;
        arguments[5] = cat2;

        return getResultSetWithProvidedColumnNames(null, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames);
    }

    /* L0 */ public String getDatabaseProductName() throws SQLServerException {
        checkClosed();
        return "Microsoft SQL Server";
    }

    /* L0 */ public String getDatabaseProductVersion() throws SQLServerException {
        checkClosed();
        return connection.sqlServerVersion;
    }

    /* L0 */ public int getDefaultTransactionIsolation() throws SQLServerException {
        checkClosed();
        return java.sql.Connection.TRANSACTION_READ_COMMITTED;
    }

    /* L0 */ public int getDriverMajorVersion() {
        return SQLJdbcVersion.major;
    }

    /* L0 */ public int getDriverMinorVersion() {
        return SQLJdbcVersion.minor;
    }

    /* L0 */ public String getDriverName() throws SQLServerException {
        checkClosed();
        return SQLServerDriver.PRODUCT_NAME;
    }

    /* L0 */ public String getDriverVersion() throws SQLServerException {

        // driver version in the Major.Minor.MMDD.Revision form
        int n = getDriverMinorVersion();
        String s = getDriverMajorVersion() + ".";
        s += "" + n;
        s = s + ".";
        s = s + SQLJdbcVersion.patch;
        s = s + ".";
        s = s + SQLJdbcVersion.build;
        return s;
    }

    /* L0 */ public java.sql.ResultSet getExportedKeys(String cat,
            String schema,
            String table) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        /*
         * sp_fkeys [ @pktable_name = ] 'pktable_name' [ , [ @pktable_owner = ] 'pktable_owner' ] [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] {
         * , [ @fktable_name = ] 'fktable_name' } [ , [ @fktable_owner = ] 'fktable_owner' ] [ , [ @fktable_qualifier = ] 'fktable_qualifier' ]
         */
        String[] arguments = new String[6];
        arguments[0] = table; // pktable_name
        arguments[1] = schema;
        arguments[2] = cat;
        arguments[3] = null; // fktable_name
        arguments[4] = null;
        arguments[5] = null;
        return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames);
    }

    /* L0 */ public String getExtraNameCharacters() throws SQLServerException {
        checkClosed();
        return "$#@";
    }

    /* L0 */ public String getIdentifierQuoteString() throws SQLServerException {
        checkClosed();
        return "\"";
    }

    /* L0 */ public java.sql.ResultSet getImportedKeys(String cat,
            String schema,
            String table) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        /*
         * sp_fkeys [ @pktable_name = ] 'pktable_name' [ , [ @pktable_owner = ] 'pktable_owner' ] [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] {
         * , [ @fktable_name = ] 'fktable_name' } [ , [ @fktable_owner = ] 'fktable_owner' ] [ , [ @fktable_qualifier = ] 'fktable_qualifier' ]
         */
        String[] arguments = new String[6];
        arguments[0] = null; // pktable_name
        arguments[1] = null;
        arguments[2] = null;
        arguments[3] = table; // fktable_name
        arguments[4] = schema;
        arguments[5] = cat;
        return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames);
    }

    private final static String[] getIndexInfoColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ NON_UNIQUE,
            /* 5 */ INDEX_QUALIFIER, /* 6 */ INDEX_NAME, /* 7 */ TYPE, /* 8 */ ORDINAL_POSITION, /* 9 */ COLUMN_NAME, /* 10 */ ASC_OR_DESC,
            /* 11 */ CARDINALITY, /* 12 */ PAGES, /* 13 */ FILTER_CONDITION};

    /* L0 */ public java.sql.ResultSet getIndexInfo(String cat,
            String schema,
            String table,
            boolean unique,
            boolean approximate) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        /*
         * sp_statistics [ @table_name = ] 'table_name' [ , [ @table_owner = ] 'owner' ] [ , [ @table_qualifier = ] 'qualifier' ] [ , [ @index_name =
         * ] 'index_name' ] [ , [ @is_unique = ] 'is_unique' ] [ , [ @accuracy = ] 'accuracy' ]
         */
        String[] arguments = new String[6];
        arguments[0] = table;
        arguments[1] = schema;
        arguments[2] = cat;
        // use default for index name
        arguments[3] = "%"; // index name % is default
        if (unique)
            arguments[4] = "Y"; // is_unique
        else
            arguments[4] = "N";
        if (approximate)
            arguments[5] = "Q";
        else
            arguments[5] = "E";
        return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_STATISTICS, arguments, getIndexInfoColumnNames);
    }

    /* L0 */ public int getMaxBinaryLiteralLength() throws SQLServerException {
        checkClosed();
        return 0;
    }

    /* L0 */ public int getMaxCatalogNameLength() throws SQLServerException {
        checkClosed();
        return 128;
    }

    /* L0 */ public int getMaxCharLiteralLength() throws SQLServerException {
        checkClosed();
        return 0;
    }

    /* L0 */ public int getMaxColumnNameLength() throws SQLServerException {
        checkClosed();
        return 128;
    }

    /* L0 */ public int getMaxColumnsInGroupBy() throws SQLServerException {
        checkClosed();
        return 0;
    }

    /* L0 */ public int getMaxColumnsInIndex() throws SQLServerException {
        checkClosed();
        return 16;
    }

    /* L0 */ public int getMaxColumnsInOrderBy() throws SQLServerException {
        checkClosed();
        return 0;
    }

    /* L0 */ public int getMaxColumnsInSelect() throws SQLServerException {
        checkClosed();
        return 4096;
    }

    /* L0 */ public int getMaxColumnsInTable() throws SQLServerException {
        checkClosed();
        return 1024;
    }

    /* L0 */ public int getMaxConnections() throws SQLServerException {
        checkClosed();
        try {
            String s = "sp_configure 'user connections'";
            SQLServerResultSet rs = getResultSetFromInternalQueries(null, s);
            if (!rs.next())
                return 0;
            return rs.getInt("maximum");
        }
        catch (SQLServerException e) {
            return 0;
        }

    }

    /* L0 */ public int getMaxCursorNameLength() throws SQLServerException {
        checkClosed();
        return 0;
    }

    /* L0 */ public int getMaxIndexLength() throws SQLServerException {
        checkClosed();
        return 900;
    }

    /* L0 */ public int getMaxProcedureNameLength() throws SQLServerException {
        checkClosed();
        return 128;
    }

    /* L0 */ public int getMaxRowSize() throws SQLServerException {
        checkClosed();
        return 8060;
    }

    /* L0 */ public int getMaxSchemaNameLength() throws SQLServerException {
        checkClosed();
        return 128;
    }

    /* L0 */ public int getMaxStatementLength() throws SQLServerException {
        checkClosed();

        // SQL Server currently limits to 64K the number of TDS packets per conversation.
        // This number multiplied by the size of each TDS packet yields the maximum total
        // size of any request to the server, which is therefore an upper bound to the
        // maximum SQL statement length.
        return 65536 * connection.getTDSPacketSize();
    }

    /* L0 */ public int getMaxStatements() throws SQLServerException {
        checkClosed();
        return 0;
    }

    /* L0 */ public int getMaxTableNameLength() throws SQLServerException {
        checkClosed();
        return 128;
    }

    /* L0 */ public int getMaxTablesInSelect() throws SQLServerException {
        checkClosed();
        return 256;
    }

    /* L0 */ public int getMaxUserNameLength() throws SQLServerException {
        checkClosed();
        return 128;
    }

    /* L0 */ public String getNumericFunctions() throws SQLServerException {
        checkClosed();
        return "ABS,ACOS,ASIN,ATAN,ATAN2,CEILING,COS,COT,DEGREES,EXP, FLOOR,LOG,LOG10,MOD,PI,POWER,RADIANS,RAND,ROUND,SIGN,SIN,SQRT,TAN,TRUNCATE";
    }

    private final static String[] getPrimaryKeysColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ COLUMN_NAME,
            /* 5 */ KEY_SEQ, /* 6 */ PK_NAME};

    /* L0 */ public java.sql.ResultSet getPrimaryKeys(String cat,
            String schema,
            String table) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        /*
         * sp_pkeys [ @table_name = ] 'name' [ , [ @table_owner = ] 'owner' ] [ , [ @table_qualifier = ] 'qualifier' ]
         */
        String[] arguments = new String[3];
        arguments[0] = table;
        arguments[1] = schema;
        arguments[2] = cat;
        return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_PKEYS, arguments, getPrimaryKeysColumnNames);
    }

    private final static String[] getProcedureColumnsColumnNames = {/* 1 */ PROCEDURE_CAT, /* 2 */ PROCEDURE_SCHEM, /* 3 */ PROCEDURE_NAME,
            /* 4 */ COLUMN_NAME, /* 5 */ COLUMN_TYPE, /* 6 */ DATA_TYPE, /* 7 */ TYPE_NAME, /* 8 */ PRECISION, /* 9 */ LENGTH, /* 10 */ SCALE,
            /* 11 */ RADIX, /* 12 */ NULLABLE, /* 13 */ REMARKS, /* 14 */ COLUMN_DEF, /* 15 */ SQL_DATA_TYPE, /* 16 */ SQL_DATETIME_SUB,
            /* 17 */ CHAR_OCTET_LENGTH, /* 18 */ ORDINAL_POSITION, /* 19 */ IS_NULLABLE};

    /* L0 */ public java.sql.ResultSet getProcedureColumns(String catalog,
            String schema,
            String proc,
            String col) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        /*
         * sp_sproc_columns [[@procedure_name =] 'name'] [,[@procedure_owner =] 'owner'] [,[@procedure_qualifier =] 'qualifier'] [,[@column_name =]
         * 'column_name'] [,[@ODBCVer =] 'ODBCVer']
         */

        String[] arguments = new String[5];

        // proc name supports escaping
        proc = EscapeIDName(proc);
        arguments[0] = proc;
        arguments[1] = schema;
        arguments[2] = catalog;
        // col name supports escaping
        col = EscapeIDName(col);
        arguments[3] = col;
        arguments[4] = "3";
        SQLServerResultSet rs = getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_SPROC_COLUMNS, arguments,
                getProcedureColumnsColumnNames);

        // Hook in a filter on the DATA_TYPE column of the result set we're
        // going to return that converts the ODBC values from sp_columns
        // into JDBC values. Also for the precision
        rs.getColumn(6).setFilter(new DataTypeFilter());
        if (connection.isKatmaiOrLater()) {
            rs.getColumn(8).setFilter(new ZeroFixupFilter());
            rs.getColumn(9).setFilter(new ZeroFixupFilter());
            rs.getColumn(17).setFilter(new ZeroFixupFilter());
        }

        return rs;
    }

    private final static String[] getProceduresColumnNames = {/* 1 */ PROCEDURE_CAT, /* 2 */ PROCEDURE_SCHEM, /* 3 */ PROCEDURE_NAME,
            /* 4 */ NUM_INPUT_PARAMS, /* 5 */ NUM_OUTPUT_PARAMS, /* 6 */ NUM_RESULT_SETS, /* 7 */ REMARKS, /* 8 */ PROCEDURE_TYPE};

    /* L0 */ public java.sql.ResultSet getProcedures(String catalog,
            String schema,
            String proc) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }

        checkClosed();
        /*
         * sp_stored_procedures [ [ @sp_name = ] 'name' ] [ , [ @sp_owner = ] 'schema'] [ , [ @sp_qualifier = ] 'qualifier' ] [ , [@fUsePattern = ]
         * 'fUsePattern' ]
         */
        String[] arguments = new String[3];
        arguments[0] = EscapeIDName(proc);
        arguments[1] = schema;
        arguments[2] = catalog;
        return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_STORED_PROCEDURES, arguments, getProceduresColumnNames);
    }

    /* L0 */ public String getProcedureTerm() throws SQLServerException {
        checkClosed();
        return "stored procedure";
    }

    public ResultSet getPseudoColumns(String catalog,
            String schemaPattern,
            String tableNamePattern,
            String columnNamePattern) throws SQLException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }

        checkClosed();

        // SQL server does not support pseudo columns for identifiers
        // as per http://msdn.microsoft.com/en-us/library/ms378445%28v=sql.110%29.aspx
        // so just return empty result set
        return getResultSetFromInternalQueries(catalog, "SELECT" +
        /* 1 */ " cast(NULL as char(1)) as TABLE_CAT," +
        /* 2 */ " cast(NULL as char(1)) as TABLE_SCHEM," +
        /* 3 */ " cast(NULL as char(1)) as TABLE_NAME," +
        /* 4 */ " cast(NULL as char(1)) as COLUMN_NAME," +
        /* 5 */ " cast(0 as int) as DATA_TYPE," +
        /* 6 */ " cast(0 as int) as COLUMN_SIZE," +
        /* 8 */ " cast(0 as int) as DECIMAL_DIGITS," +
        /* 9 */ " cast(0 as int) as NUM_PREC_RADIX," +
        /* 10 */ " cast(NULL as char(1)) as COLUMN_USAGE," +
        /* 11 */ " cast(NULL as char(1)) as REMARKS," +
        /* 12 */ " cast(0 as int) as CHAR_OCTET_LENGTH," +
        /* 13 */ " cast(NULL as char(1)) as IS_NULLABLE" + " where 0 = 1");
    }

    /* L0 */ public java.sql.ResultSet getSchemas() throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        return getSchemasInternal(null, null);

    }

    private java.sql.ResultSet getSchemasInternal(String catalog,
            String schemaPattern) throws SQLServerException {

        String s;
        // The schemas that return null for catalog name, these are prebuilt schemas shipped by SQLServer, if SQLServer adds anymore of these
        // we need to add them here.
        String constSchemas = " ('dbo', 'guest','INFORMATION_SCHEMA','sys','db_owner', 'db_accessadmin', 'db_securityadmin', 'db_ddladmin' "
                + " ,'db_backupoperator','db_datareader','db_datawriter','db_denydatareader','db_denydatawriter') ";

        String schema = "sys.schemas";
        String schemaName = "sys.schemas.name";
        if (null != catalog && catalog.length() != 0) {
            schema = catalog + "." + schema;
            schemaName = catalog + "." + schemaName;
        }

        // The common schemas need to be under null catalog name however the schemas specific to the particular catalog has to have the current
        // catalog name
        // to achive this, first we figure out the common schemas by intersecting current catalogs schemas with the const schemas (ie builtinSchemas)
        s = "select " + schemaName + " 'TABLE_SCHEM',";
        if (null != catalog && catalog.length() == 0) {
            s += "null 'TABLE_CATALOG' ";
        }
        else {
            s += " CASE WHEN " + schemaName + "  IN " + constSchemas + " THEN null ELSE ";
            if (null != catalog && catalog.length() != 0) {
                s += "'" + catalog + "' ";
            }
            else
                s += " DB_NAME() ";

            s += " END 'TABLE_CATALOG' ";
        }
        s += "   from " + schema;

        // Handle the case when catalog is empty this means common schemas only
        //
        if (null != catalog && catalog.length() == 0) {
            if (null != schemaPattern)
                s += " where " + schemaName + " like ?  and ";
            else
                s += " where ";
            s += schemaName + " in " + constSchemas;
        }
        else if (null != schemaPattern)
            s += " where " + schemaName + " like ?  ";

        s += " order by 2, 1";
        if (logger.isLoggable(java.util.logging.Level.FINE)) {
            logger.fine(toString() + " schema query (" + s + ")");
        }
        SQLServerResultSet rs;
        if (null == schemaPattern) {
            catalog = null;
            rs = getResultSetFromInternalQueries(catalog, s);
        }
        else {

            // The prepared statement is not closed after execution.
            // Yes we will "leak a server handle" per execution but the connection closure will release them
            //
            SQLServerPreparedStatement ps = (SQLServerPreparedStatement) connection.prepareStatement(s);
            ps.setString(1, schemaPattern);
            rs = (SQLServerResultSet) ps.executeQueryInternal();
        }
        return rs;
    }

    public java.sql.ResultSet getSchemas(String catalog,
            String schemaPattern) throws SQLException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        return getSchemasInternal(catalog, schemaPattern);
    }

    /* L0 */ public String getSchemaTerm() throws SQLServerException {
        checkClosed();
        return "schema";
    }

    /* L0 */ public String getSearchStringEscape() throws SQLServerException {
        checkClosed();
        return "\\";
    }

    /* L0 */ public String getSQLKeywords() throws SQLServerException {
        checkClosed();
        return "BACKUP,BREAK,BROWSE,BULK,CHECKPOINT,CLUSTERED,COMPUTE,CONTAINS,CONTAINSTABLE,DATABASE,DBCC,DENY,DISK,DISTRIBUTED,DUMMY,DUMP,ERRLVL,EXIT,FILE,FILLFACTOR,FREETEXT,FREETEXTTABLE,FUNCTION,HOLDLOCK,IDENTITY_INSERT,IDENTITYCOL,IF,KILL,LINENO,LOAD,NOCHECK,NONCLUSTERED,OFF,OFFSETS,OPENDATASOURCE,OPENQUERY,OPENROWSET,OPENXML,OVER,PERCENT,PLAN,PRINT,PROC,RAISERROR,READTEXT,RECONFIGURE,REPLICATION,RESTORE,RETURN,ROWCOUNT,ROWGUIDCOL,RULE,SAVE,SETUSER,SHUTDOWN,STATISTICS,TEXTSIZE,TOP,TRAN,TRIGGER,TRUNCATE,TSEQUAL,UPDATETEXT,USE,WAITFOR,WHILE,WRITETEXT";
    }

    /* L0 */ public String getStringFunctions() throws SQLServerException {
        checkClosed();
        return "ASCII,CHAR,CONCAT, DIFFERENCE,INSERT,LCASE,LEFT,LENGTH,LOCATE,LTRIM,REPEAT,REPLACE,RIGHT,RTRIM,SOUNDEX,SPACE,SUBSTRING,UCASE";
    }

    /* L0 */ public String getSystemFunctions() throws SQLServerException {
        checkClosed();
        return "DATABASE,IFNULL,USER"; // The functions no reinstated after the CTS certification.
    }

    private final static String[] getTablePrivilegesColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ GRANTOR,
            /* 5 */ GRANTEE, /* 6 */ PRIVILEGE, /* 7 */ IS_GRANTABLE};

    /* L0 */ public java.sql.ResultSet getTablePrivileges(String catalog,
            String schema,
            String table) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        table = EscapeIDName(table);
        schema = EscapeIDName(schema);
        /*
         * sp_table_privileges [ @table_name = ] 'table_name' [ , [ @table_owner = ] 'table_owner' ] [ , [ @table_qualifier = ] 'table_qualifier' ] [
         * , [@fUsePattern =] 'fUsePattern']
         */
        String[] arguments = new String[3];
        arguments[0] = table;
        arguments[1] = schema;
        arguments[2] = catalog;

        return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_TABLE_PRIVILEGES, arguments, getTablePrivilegesColumnNames);
    }

    /* L0 */ public java.sql.ResultSet getTableTypes() throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        String s = "SELECT 'VIEW' 'TABLE_TYPE' UNION SELECT 'TABLE' UNION SELECT 'SYSTEM TABLE'";
        SQLServerResultSet rs = getResultSetFromInternalQueries(null, s);
        return rs;
    }

    /* L0 */ public String getTimeDateFunctions() throws SQLServerException {
        checkClosed();
        return "CURDATE,CURTIME,DAYNAME,DAYOFMONTH,DAYOFWEEK,DAYOFYEAR,HOUR,MINUTE,MONTH,MONTHNAME,NOW,QUARTER,SECOND,TIMESTAMPADD,TIMESTAMPDIFF,WEEK,YEAR";
    }

    /* L0 */ public java.sql.ResultSet getTypeInfo() throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();

        SQLServerResultSet rs;
        // We support only sql2k5 and above
        if (connection.isKatmaiOrLater())
            rs = getResultSetFromInternalQueries(null, "sp_datatype_info_100 @ODBCVer=3");
        else
            rs = getResultSetFromInternalQueries(null, "sp_datatype_info @ODBCVer=3");

        rs.setColumnName(11, "FIXED_PREC_SCALE");
        // Hook in a filter on the DATA_TYPE column of the result set we're
        // going to return that converts the ODBC values from sp_columns
        // into JDBC values.
        rs.getColumn(2).setFilter(new DataTypeFilter());
        return rs;
    }

    /* L0 */ public String getURL() throws SQLServerException {
        checkClosed();
        // Build up the URL with the connection properties do not hand out user ID and password
        StringBuilder url = new StringBuilder();
        // get the properties collection from the connection.
        Properties props = connection.activeConnectionProperties;
        DriverPropertyInfo[] info = SQLServerDriver.getPropertyInfoFromProperties(props);
        String serverName = null;
        String portNumber = null;
        String instanceName = null;

        // build the connection string without the server name, instance name and port number as these go in the front
        int index = info.length;
        while (--index >= 0) {
            String name = info[index].name;

            // making sure no security info is exposed.
            if (!name.equals(SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.toString())
                    && !name.equals(SQLServerDriverStringProperty.USER.toString()) && !name.equals(SQLServerDriverStringProperty.PASSWORD.toString())
                    && !name.equals(SQLServerDriverStringProperty.KEY_STORE_SECRET.toString())) {
                String val = info[index].value;
                // skip empty strings
                if (0 != val.length()) {
                    // special case these server name, instance name and port number as these go in the front
                    if (name.equals(SQLServerDriverStringProperty.SERVER_NAME.toString())) {
                        serverName = val;
                    }
                    else if (name.equals(SQLServerDriverStringProperty.INSTANCE_NAME.toString())) {
                        instanceName = val;
                    }
                    else if (name.equals(SQLServerDriverIntProperty.PORT_NUMBER.toString())) {
                        portNumber = val;
                    }
                    else {
                        // build name value pairs separated by a semi colon
                        url.append(name);
                        url.append("=");
                        url.append(val);
                        url.append(";");
                    }
                }
            }

        }
        // insert the special items in the front in the reverse order.
        // This way we will get the expected form as below.
        // MYSERVER\INSTANCEFOO:1433
        // port number first, we should always have port number
        url.insert(0, ";");
        url.insert(0, portNumber);
        url.insert(0, ":");
        if (null != instanceName) {
            url.insert(0, instanceName);
            url.insert(0, "\\");
        }
        url.insert(0, serverName);

        url.insert(0, urlprefix); // insert the prefix at the front.
        return (url.toString());
    }

    /* L0 */ public String getUserName() throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        SQLServerStatement s = null;
        SQLServerResultSet rs = null;
        String result = "";

        try {
            s = (SQLServerStatement) connection.createStatement();
            rs = s.executeQueryInternal("select system_user");
            // Select system_user will always return a row.
            boolean next = rs.next();
            assert next;

            result = rs.getString(1);
        }
        finally {
            if (rs != null) {
                rs.close();
            }
            if (s != null) {
                s.close();
            }
        }
        return result;
    }

    private final static String[] getVersionColumnsColumnNames = {/* 1 */ SCOPE, /* 2 */ COLUMN_NAME, /* 3 */ DATA_TYPE, /* 4 */ TYPE_NAME,
            /* 5 */ COLUMN_SIZE, /* 6 */ BUFFER_LENGTH, /* 7 */ DECIMAL_DIGITS, /* 8 */ PSEUDO_COLUMN};

    /* L0 */ public java.sql.ResultSet getVersionColumns(String catalog,
            String schema,
            String table) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        /*
         * sp_special_columns [@table_name =] 'table_name' [,[@table_owner =] 'table_owner'] [,[@qualifier =] 'qualifier'] [,[@col_type =] 'col_type']
         * [,[@scope =] 'scope'] [,[@nullable =] 'nullable'] [,[@ODBCVer =] 'ODBCVer'] ;
         */
        String[] arguments = new String[7];
        arguments[0] = table;
        arguments[1] = schema;
        arguments[2] = catalog;
        arguments[3] = "V"; // col type
        arguments[4] = "T"; // scope
        arguments[5] = "U"; // nullable
        arguments[6] = "3"; // odbc ver
        SQLServerResultSet rs = getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_SPECIAL_COLUMNS, arguments,
                getVersionColumnsColumnNames);

        // Hook in a filter on the DATA_TYPE column of the result set we're
        // going to return that converts the ODBC values from sp_columns
        // into JDBC values.
        rs.getColumn(3).setFilter(new DataTypeFilter());
        return rs;
    }

    /* L0 */ public boolean isCatalogAtStart() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean isReadOnly() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean nullPlusNonNullIsNull() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean nullsAreSortedAtEnd() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean nullsAreSortedAtStart() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean nullsAreSortedHigh() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean nullsAreSortedLow() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean storesLowerCaseIdentifiers() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean storesLowerCaseQuotedIdentifiers() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean storesMixedCaseIdentifiers() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean storesMixedCaseQuotedIdentifiers() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean storesUpperCaseIdentifiers() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean storesUpperCaseQuotedIdentifiers() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsAlterTableWithAddColumn() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsAlterTableWithDropColumn() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsANSI92EntryLevelSQL() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsANSI92FullSQL() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsANSI92IntermediateSQL() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsCatalogsInDataManipulation() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsCatalogsInIndexDefinitions() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsCatalogsInProcedureCalls() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsCatalogsInTableDefinitions() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsColumnAliasing() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsConvert() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsConvert(int fromType,
            int toType) throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsCoreSQLGrammar() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsCorrelatedSubqueries() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsDataManipulationTransactionsOnly() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsDifferentTableCorrelationNames() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsExpressionsInOrderBy() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsExtendedSQLGrammar() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsFullOuterJoins() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsGroupBy() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsGroupByBeyondSelect() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsGroupByUnrelated() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsIntegrityEnhancementFacility() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsLikeEscapeClause() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsLimitedOuterJoins() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsMinimumSQLGrammar() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsMixedCaseIdentifiers() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsMixedCaseQuotedIdentifiers() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsMultipleResultSets() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsMultipleTransactions() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsNonNullableColumns() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsOpenCursorsAcrossCommit() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsOpenCursorsAcrossRollback() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsOpenStatementsAcrossCommit() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsOpenStatementsAcrossRollback() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsOrderByUnrelated() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsOuterJoins() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsPositionedDelete() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsPositionedUpdate() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSchemasInDataManipulation() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSchemasInIndexDefinitions() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSchemasInPrivilegeDefinitions() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSchemasInProcedureCalls() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSchemasInTableDefinitions() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSelectForUpdate() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsStoredProcedures() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSubqueriesInComparisons() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSubqueriesInExists() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSubqueriesInIns() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsSubqueriesInQuantifieds() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsTableCorrelationNames() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsTransactionIsolationLevel(int level) throws SQLServerException {
        checkClosed();
        switch (level) {
            case Connection.TRANSACTION_READ_UNCOMMITTED:
            case Connection.TRANSACTION_READ_COMMITTED:
            case Connection.TRANSACTION_REPEATABLE_READ:
            case Connection.TRANSACTION_SERIALIZABLE:
            case SQLServerConnection.TRANSACTION_SNAPSHOT:
                return true;
        }
        return false;
    }

    /* L0 */ public boolean supportsTransactions() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsUnion() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean supportsUnionAll() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public boolean usesLocalFilePerTable() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean usesLocalFiles() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L0 */ public boolean supportsResultSetType(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        switch (type) {
            case ResultSet.TYPE_FORWARD_ONLY:
            case ResultSet.TYPE_SCROLL_INSENSITIVE:
            case ResultSet.TYPE_SCROLL_SENSITIVE:
                // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: insensitive synonym
                // case SQLServerResultSet.TYPE_SS_SCROLL_KEYSET: sensitive synonym
            case SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY:
            case SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY:
            case SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC:
                return true;
        }
        return false;
    }

    /* L0 */ public boolean supportsResultSetConcurrency(int type,
            int concurrency) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        checkConcurrencyType(concurrency);
        switch (type) {
            case ResultSet.TYPE_FORWARD_ONLY:
            case ResultSet.TYPE_SCROLL_SENSITIVE:
                // case SQLServerResultSet.TYPE_SS_SCROLL_KEYSET: sensitive synonym
            case SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC:
            case SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY:
                return true;
            case ResultSet.TYPE_SCROLL_INSENSITIVE:
                // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: sensitive synonym
            case SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY:
                if (ResultSet.CONCUR_READ_ONLY == concurrency)
                    return true;
                else
                    return false;
        }
        // per spec if we do not know we do not support.
        return false;
    }

    /* L0 */ public boolean ownUpdatesAreVisible(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type
                || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type
                || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type)
            return true;
        return false;
    }

    /* L0 */ public boolean ownDeletesAreVisible(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type
                || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type
                || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type)
            return true;
        return false;
    }

    /* L0 */ public boolean ownInsertsAreVisible(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type
                || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type
                || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type)
            return true;
        return false;
    }

    /* L0 */ public boolean othersUpdatesAreVisible(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type
                || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type
                || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type)
            return true;
        return false;
    }

    /* L0 */ public boolean othersDeletesAreVisible(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type
                || SQLServerResultSet.TYPE_SCROLL_SENSITIVE == type || SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type
                || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type)
            return true;
        return false;
    }

    /* L0 */ public boolean othersInsertsAreVisible(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        if (type == SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC || SQLServerResultSet.TYPE_FORWARD_ONLY == type
                || SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY == type)
            return true;
        return false;
    }

    /* L0 */ public boolean updatesAreDetected(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        return false;
    }

    /* L0 */ public boolean deletesAreDetected(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        if (SQLServerResultSet.TYPE_SS_SCROLL_KEYSET == type)
            return true;
        else
            return false;
    }

    // Check the result types to make sure the user does not pass a bad value.
    /* L0 */ private void checkResultType(int type) throws SQLServerException {
        switch (type) {
            case ResultSet.TYPE_FORWARD_ONLY:
            case ResultSet.TYPE_SCROLL_INSENSITIVE:
            case ResultSet.TYPE_SCROLL_SENSITIVE:
                // case SQLServerResultSet.TYPE_SS_SCROLL_STATIC: synonym TYPE_SCROLL_INSENSITIVE
                // case SQLServerResultSet.TYPE_SS_SCROLL_KEYSET: synonym TYPE_SCROLL_SENSITIVE
            case SQLServerResultSet.TYPE_SS_DIRECT_FORWARD_ONLY:
            case SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY:
            case SQLServerResultSet.TYPE_SS_SCROLL_DYNAMIC:
                return;
        }
        // if the value is outside of the valid values throw error.
        MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument"));
        Object[] msgArgs = {type};
        throw new SQLServerException(null, form.format(msgArgs), null, 0, true);
    }

    // Check the concurrency values and make sure the value is a supported value.
    /* L0 */ private void checkConcurrencyType(int type) throws SQLServerException {
        switch (type) {
            case ResultSet.CONCUR_READ_ONLY:
            case ResultSet.CONCUR_UPDATABLE:
                // case SQLServerResultSet.CONCUR_SS_OPTIMISTIC_CC: synonym CONCUR_UPDATABLE
            case SQLServerResultSet.CONCUR_SS_SCROLL_LOCKS:
            case SQLServerResultSet.CONCUR_SS_OPTIMISTIC_CCVAL:
                return;
        }
        // if the value is outside of the valid values throw error.
        MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument"));
        Object[] msgArgs = {type};
        throw new SQLServerException(null, form.format(msgArgs), null, 0, true);
    }

    /* L0 */ public boolean insertsAreDetected(int type) throws SQLServerException {
        checkClosed();
        checkResultType(type);
        return false;
    }

    /* L0 */ public boolean supportsBatchUpdates() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L0 */ public java.sql.ResultSet getUDTs(String catalog,
            String schemaPattern,
            String typeNamePattern,
            int[] types) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        return getResultSetFromInternalQueries(catalog, "SELECT" +
        /* 1 */ " cast(NULL as char(1)) as TYPE_CAT," +
        /* 2 */ " cast(NULL as char(1)) as TYPE_SCHEM," +
        /* 3 */ " cast(NULL as char(1)) as TYPE_NAME," +
        /* 4 */ " cast(NULL as char(1)) as CLASS_NAME," +
        /* 5 */ " cast(0 as int) as DATA_TYPE," +
        /* 6 */ " cast(NULL as char(1)) as REMARKS," +
        /* 7 */ " cast(0 as smallint) as BASE_TYPE" + " where 0 = 1");
    }

    /* L0 */ public java.sql.Connection getConnection() throws SQLServerException {
        checkClosed();
        return connection.getConnection();
    }

    /* JDBC 3.0 */

    /* L3 */ public int getSQLStateType() throws SQLServerException {
        checkClosed();
        if (connection != null && connection.xopenStates)
            return sqlStateXOpen;
        else
            return sqlStateSQL99;
    }

    /* L3 */ public int getDatabaseMajorVersion() throws SQLServerException {
        checkClosed();
        String s = connection.sqlServerVersion;
        int p = s.indexOf('.');
        if (p > 0)
            s = s.substring(0, p);
        try {
            return new Integer(s);
        }
        catch (NumberFormatException e) {
            return 0;
        }
    }

    /* L3 */ public int getDatabaseMinorVersion() throws SQLServerException {
        checkClosed();
        String s = connection.sqlServerVersion;
        int p = s.indexOf('.');
        int q = s.indexOf('.', p + 1);
        if (p > 0 && q > 0)
            s = s.substring(p + 1, q);
        try {
            return new Integer(s);
        }
        catch (NumberFormatException e) {
            return 0;
        }
    }

    /* L3 */ public int getJDBCMajorVersion() throws SQLServerException {
        checkClosed();
        return DriverJDBCVersion.major;
    }

    /* L3 */ public int getJDBCMinorVersion() throws SQLServerException {
        checkClosed();
        return DriverJDBCVersion.minor;
    }

    /* L3 */ public int getResultSetHoldability() throws SQLServerException {
        checkClosed();
        return ResultSet.HOLD_CURSORS_OVER_COMMIT; // Hold over commit is the default for SQL Server
    }

    public RowIdLifetime getRowIdLifetime() throws SQLException {
        checkClosed();
        return RowIdLifetime.ROWID_UNSUPPORTED;
    }

    /* L3 */ public boolean supportsResultSetHoldability(int holdability) throws SQLServerException {
        checkClosed();
        if (ResultSet.HOLD_CURSORS_OVER_COMMIT == holdability || ResultSet.CLOSE_CURSORS_AT_COMMIT == holdability) {
            return true; // supported one a per connection level only, not statement by statement
        }

        // if the value is outside of the valid values throw error.
        MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument"));
        Object[] msgArgs = {holdability};
        throw new SQLServerException(null, form.format(msgArgs), null, 0, true);
    }

    /* L3 */ public ResultSet getAttributes(String catalog,
            String schemaPattern,
            String typeNamePattern,
            String attributeNamePattern) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        return getResultSetFromInternalQueries(catalog, "SELECT" +
        /* 1 */ " cast(NULL as char(1)) as TYPE_CAT," +
        /* 2 */ " cast(NULL as char(1)) as TYPE_SCHEM," +
        /* 3 */ " cast(NULL as char(1)) as TYPE_NAME," +
        /* 4 */ " cast(NULL as char(1)) as ATTR_NAME," +
        /* 5 */ " cast(0 as int) as DATA_TYPE," +
        /* 6 */ " cast(NULL as char(1)) as ATTR_TYPE_NAME," +
        /* 7 */ " cast(0 as int) as ATTR_SIZE," +
        /* 8 */ " cast(0 as int) as DECIMAL_DIGITS," +
        /* 9 */ " cast(0 as int) as NUM_PREC_RADIX," +
        /* 10 */ " cast(0 as int) as NULLABLE," +
        /* 11 */ " cast(NULL as char(1)) as REMARKS," +
        /* 12 */ " cast(NULL as char(1)) as ATTR_DEF," +
        /* 13 */ " cast(0 as int) as SQL_DATA_TYPE," +
        /* 14 */ " cast(0 as int) as SQL_DATETIME_SUB," +
        /* 15 */ " cast(0 as int) as CHAR_OCTET_LENGTH," +
        /* 16 */ " cast(0 as int) as ORDINAL_POSITION," +
        /* 17 */ " cast(NULL as char(1)) as IS_NULLABLE," +
        /* 18 */ " cast(NULL as char(1)) as SCOPE_CATALOG," +
        /* 19 */ " cast(NULL as char(1)) as SCOPE_SCHEMA," +
        /* 20 */ " cast(NULL as char(1)) as SCOPE_TABLE," +
        /* 21 */ " cast(0 as smallint) as SOURCE_DATA_TYPE" + " where 0 = 1");
    }

    /* L3 */ public ResultSet getSuperTables(String catalog,
            String schemaPattern,
            String tableNamePattern) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        return getResultSetFromInternalQueries(catalog, "SELECT" +
        /* 1 */ " cast(NULL as char(1)) as TYPE_CAT," +
        /* 2 */ " cast(NULL as char(1)) as TYPE_SCHEM," +
        /* 3 */ " cast(NULL as char(1)) as TYPE_NAME," +
        /* 4 */ " cast(NULL as char(1)) as SUPERTABLE_NAME" + " where 0 = 1");
    }

    /* L3 */ public ResultSet getSuperTypes(String catalog,
            String schemaPattern,
            String typeNamePattern) throws SQLServerException {
        if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
            loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
        }
        checkClosed();
        return getResultSetFromInternalQueries(catalog, "SELECT" +
        /* 1 */ " cast(NULL as char(1)) as TYPE_CAT," +
        /* 2 */ " cast(NULL as char(1)) as TYPE_SCHEM," +
        /* 3 */ " cast(NULL as char(1)) as TYPE_NAME," +
        /* 4 */ " cast(NULL as char(1)) as SUPERTYPE_CAT," +
        /* 5 */ " cast(NULL as char(1)) as SUPERTYPE_SCHEM," +
        /* 6 */ " cast(NULL as char(1)) as SUPERTYPE_NAME" + " where 0 = 1");
    }

    /* L3 */ public boolean supportsGetGeneratedKeys() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L3 */ public boolean supportsMultipleOpenResults() throws SQLServerException {
        checkClosed();
        return false;
    }

    /* L3 */ public boolean supportsNamedParameters() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L3 */ public boolean supportsSavepoints() throws SQLServerException {
        checkClosed();
        return true;
    }

    /* L3 */ public boolean supportsStatementPooling() throws SQLException {
        checkClosed();
        return false;
    }

    public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException {
        checkClosed();
        return true;
    }

    /* L3 */ public boolean locatorsUpdateCopy() throws SQLException {
        checkClosed();
        return true;
    }
}

// Filter to convert DATA_TYPE column values from the ODBC types
// returned by SQL Server to their equivalent JDBC types.
final class DataTypeFilter extends IntColumnFilter {
    private final static int ODBC_SQL_GUID = -11;
    private final static int ODBC_SQL_WCHAR = -8;
    private final static int ODBC_SQL_WVARCHAR = -9;
    private final static int ODBC_SQL_WLONGVARCHAR = -10;
    private final static int ODBC_SQL_FLOAT = 6;
    private final static int ODBC_SQL_TIME = -154;
    private final static int ODBC_SQL_XML = -152;
    private final static int ODBC_SQL_UDT = -151;

    int oneValueToAnother(int odbcType) {
        switch (odbcType) {
            case ODBC_SQL_FLOAT:
                return JDBCType.DOUBLE.asJavaSqlType();
            case ODBC_SQL_GUID:
                return JDBCType.CHAR.asJavaSqlType();
            case ODBC_SQL_WCHAR:
                return JDBCType.NCHAR.asJavaSqlType();
            case ODBC_SQL_WVARCHAR:
                return JDBCType.NVARCHAR.asJavaSqlType();
            case ODBC_SQL_WLONGVARCHAR:
                return JDBCType.LONGNVARCHAR.asJavaSqlType();
            case ODBC_SQL_TIME:
                return JDBCType.TIME.asJavaSqlType();
            case ODBC_SQL_XML:
                return SSType.XML.getJDBCType().asJavaSqlType();
            case ODBC_SQL_UDT:
                return SSType.UDT.getJDBCType().asJavaSqlType();

            default:
                return odbcType;
        }
    }

}

class ZeroFixupFilter extends IntColumnFilter {
    int oneValueToAnother(int precl) {
        if (0 == precl)
            return DataTypes.MAX_VARTYPE_MAX_BYTES;
        else
            return precl;
    }
}

// abstract class converts one value to another solely based on the column integer value
// apply to integer columns only
abstract class IntColumnFilter extends ColumnFilter {
    abstract int oneValueToAnother(int value);

    final Object apply(Object value,
            JDBCType asJDBCType) throws SQLServerException {
        if (value == null)
            return value;
        // Assumption: values will only be requested in integral or textual format
        // (i.e. not as float, double, BigDecimal, Boolean or bytes). A request to return
        // a value as anything else results in an exception being thrown.

        switch (asJDBCType) {
            case INTEGER:
                return oneValueToAnother((Integer) value);
            case SMALLINT: // small and tinyint returned as short
            case TINYINT:
                return (short) oneValueToAnother(((Short) value).intValue());
            case BIGINT:
                return (long) oneValueToAnother(((Long) value).intValue());
            case CHAR:
            case VARCHAR:
            case LONGVARCHAR:
                return Integer.toString(oneValueToAnother(Integer.parseInt((String) value)));
            default:
                DataTypes.throwConversionError("int", asJDBCType.toString());
                return value;
        }
    }

}

// Filter to convert int identity column values from 0,1 to YES, NO
// There is a mismatch between what the stored proc returns and what the
// JDBC spec expects.
class IntColumnIdentityFilter extends ColumnFilter {
    private static String zeroOneToYesNo(int i) {
        return 0 == i ? "NO" : "YES";
    }

    final Object apply(Object value,
            JDBCType asJDBCType) throws SQLServerException {
        if (value == null)
            return value;
        // Assumption: values will only be requested in integral or textual format
        // (i.e. not as float, double, BigDecimal, Boolean or bytes). A request to return
        // a value as anything else results in an exception being thrown.

        switch (asJDBCType) {
            case INTEGER:
            case SMALLINT:
                // This is a way for us to make getObject return a string, not an
                // integer. What this means is that getInt/getShort also will return a string.
                // However the identity column in the JDBC spec is supposed to return a
                // string by default. To get to that default behavior right we are deliberately breaking
                // the getInt/getShort behavior which should really error anyways. Only thing is that
                // the user will get a cast exception in this case.
                assert (value instanceof Number);
                return zeroOneToYesNo(((Number) value).intValue());
            case CHAR:
            case VARCHAR:
            case LONGVARCHAR:
                assert (value instanceof String);
                return zeroOneToYesNo(Integer.parseInt((String) value));
            default:
                DataTypes.throwConversionError("char", asJDBCType.toString());
                return value;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy