/* * 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<CallableHandles, HandleAssociation> handleMap = new EnumMap<CallableHandles, HandleAssociation>(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> T unwrap(Class<T> 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 = {new Integer(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 = {new Integer(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).intValue(); } 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).intValue(); } 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 = {new Integer(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 new Integer(oneValueToAnother(((Integer) value).intValue())); case SMALLINT: // small and tinyint returned as short case TINYINT: return new Short((short) oneValueToAnother(((Short) value).intValue())); case BIGINT: return new 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; } } }