From dfdbf76488743d346e5aaac9bf8ed07f1628a84a Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Thu, 8 Feb 2024 17:14:29 -0800 Subject: [PATCH 01/10] ResultSetMetadata of the ResultSet from DatabaseMetadata.getColumns() is Incorrect for Some Columns (1751) Fix ensures the correct data types for each metadata column match the data type from the SQL Server and the JDBC spec --- .../jdbc/SQLServerDatabaseMetaData.java | 88 +++++++++++++++---- .../DatabaseMetaDataTest.java | 84 ++++++++++++++++++ 2 files changed, 154 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 1d2868879..6af0b2f4a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -257,12 +257,18 @@ private void checkClosed() throws SQLServerException { private static final String IS_GENERATEDCOLUMN = "IS_GENERATEDCOLUMN"; private static final String IS_AUTOINCREMENT = "IS_AUTOINCREMENT"; private static final String ACTIVITY_ID = " ActivityId: "; + + private static final String NVARCHAR = "NVARCHAR"; + private static final String VARCHAR = "VARCHAR"; + private static final String INTEGER = "INT"; + private static final String SMALLINT = "SMALLINT"; private static final String SQL_KEYWORDS = createSqlKeyWords(); // Use LinkedHashMap to force retrieve elements in order they were inserted /** getColumns columns */ private LinkedHashMap getColumnsDWColumns = null; + private LinkedHashMap getTypesDWColumns = null; /** getImportedKeys columns */ private volatile LinkedHashMap getImportedKeysDWColumns; private static final Lock LOCK = new ReentrantLock(); @@ -630,15 +636,18 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table + "INSERT INTO @mssqljdbc_temp_sp_columns_result EXEC sp_columns_100 ?,?,?,?,?,?;" - + "SELECT TABLE_QUALIFIER AS TABLE_CAT, TABLE_OWNER AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, DATA_TYPE," - + "TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, SCALE AS DECIMAL_DIGITS, RADIX AS NUM_PREC_RADIX," - + "NULLABLE, REMARKS, COLUMN_DEF, SQL_DATA_TYPE, SQL_DATETIME_SUB, CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE," - + "NULL AS SCOPE_CATALOG, NULL AS SCOPE_SCHEMA, NULL AS SCOPE_TABLE, SS_DATA_TYPE AS SOURCE_DATA_TYPE," - + "CASE SS_IS_IDENTITY WHEN 0 THEN 'NO' WHEN 1 THEN 'YES' WHEN '' THEN '' END AS IS_AUTOINCREMENT," - + "CASE SS_IS_COMPUTED WHEN 0 THEN 'NO' WHEN 1 THEN 'YES' WHEN '' THEN '' END AS IS_GENERATEDCOLUMN, " - + "SS_IS_SPARSE, SS_IS_COLUMN_SET, SS_UDT_CATALOG_NAME, SS_UDT_SCHEMA_NAME, SS_UDT_ASSEMBLY_TYPE_NAME," - + "SS_XML_SCHEMACOLLECTION_CATALOG_NAME, SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, SS_XML_SCHEMACOLLECTION_NAME " - + "FROM @mssqljdbc_temp_sp_columns_result ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION;"; + + "SELECT TABLE_QUALIFIER AS TABLE_CAT, TABLE_OWNER AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, " + + "CAST (DATA_TYPE AS INT) AS DATA_TYPE,TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, " + + "CAST(SCALE AS INT) AS DECIMAL_DIGITS, CAST(RADIX AS INT) AS NUM_PREC_RADIX,CAST(NULLABLE AS INT) AS NULLABLE, " + + "CAST(REMARKS AS VARCHAR) AS REMARKS, COLUMN_DEF, CAST(SQL_DATA_TYPE AS INT) AS SQL_DATA_TYPE, " + + "CAST(SQL_DATETIME_SUB AS INT) AS SQL_DATETIME_SUB, CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE," + + "CAST(NULL AS VARCHAR) AS SCOPE_CATALOG, CAST(NULL AS VARCHAR) AS SCOPE_SCHEMA, CAST(NULL AS VARCHAR) AS SCOPE_TABLE, " + + "CAST(SS_DATA_TYPE AS SMALLINT) AS SOURCE_DATA_TYPE, " + + "CASE SS_IS_IDENTITY WHEN 0 THEN 'NO' WHEN 1 THEN 'YES' WHEN '' THEN '' END AS IS_AUTOINCREMENT," + + "CASE SS_IS_COMPUTED WHEN 0 THEN 'NO' WHEN 1 THEN 'YES' WHEN '' THEN '' END AS IS_GENERATEDCOLUMN, " + + "SS_IS_SPARSE, SS_IS_COLUMN_SET, SS_UDT_CATALOG_NAME, SS_UDT_SCHEMA_NAME, SS_UDT_ASSEMBLY_TYPE_NAME," + + "SS_XML_SCHEMACOLLECTION_CATALOG_NAME, SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, SS_XML_SCHEMACOLLECTION_NAME " + + "FROM @mssqljdbc_temp_sp_columns_result ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION;"; SQLServerResultSet rs = null; PreparedStatement pstmt = (SQLServerPreparedStatement) this.connection.prepareStatement(spColumnsSql); pstmt.closeOnCompletion(); @@ -721,6 +730,48 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table getColumnsDWColumns.put(27, SS_XML_SCHEMACOLLECTION_SCHEMA_NAME); getColumnsDWColumns.put(28, SS_XML_SCHEMACOLLECTION_NAME); } + if (null == getTypesDWColumns) { + getTypesDWColumns = new LinkedHashMap<>(); + getTypesDWColumns.put(1, NVARCHAR); // TABLE_CAT + getTypesDWColumns.put(2, NVARCHAR); // TABLE_SCHEM + getTypesDWColumns.put(3, NVARCHAR); // TABLE_NAME + getTypesDWColumns.put(4, NVARCHAR); // COLUMN_NAME + getTypesDWColumns.put(5, INTEGER); // DATA_TYPE + getTypesDWColumns.put(6, NVARCHAR); // TYPE_NAME + getTypesDWColumns.put(7, INTEGER); // COLUMN_SIZE + getTypesDWColumns.put(8, INTEGER); // BUFFER_LENGTH + getTypesDWColumns.put(9, INTEGER); // DECIMAL_DIGITS + getTypesDWColumns.put(10, INTEGER); // NUM_PREC_RADIX + getTypesDWColumns.put(11, INTEGER); // NULLABLE + getTypesDWColumns.put(12, VARCHAR); // REMARKS + getTypesDWColumns.put(13, NVARCHAR); // COLUMN_DEF + getTypesDWColumns.put(14, INTEGER); // SQL_DATA_TYPE + getTypesDWColumns.put(15, INTEGER); // SQL_DATETIME_SUB + getTypesDWColumns.put(16, INTEGER); // CHAR_OCTET_LENGTH + getTypesDWColumns.put(17, INTEGER); // ORDINAL_POSITION + getTypesDWColumns.put(18, VARCHAR); // IS_NULLABLE + /* + * Use negative value keys to indicate that this column doesn't exist in SQL Server and should just + * be queried as 'NULL' + */ + getTypesDWColumns.put(-1, VARCHAR); // SCOPE_CATALOG + getTypesDWColumns.put(-2, VARCHAR); // SCOPE_SCHEMA + getTypesDWColumns.put(-3, VARCHAR); // SCOPE_TABLE + getTypesDWColumns.put(29, SMALLINT); // SOURCE_DATA_TYPE + getTypesDWColumns.put(22, VARCHAR); // IS_AUTOINCREMENT + getTypesDWColumns.put(21, VARCHAR); // IS_GENERATEDCOLUMN + getTypesDWColumns.put(19, SMALLINT); // SS_IS_SPARSE + getTypesDWColumns.put(20, SMALLINT); // SS_IS_COLUMN_SET + getTypesDWColumns.put(23, NVARCHAR); // SS_UDT_CATALOG_NAME + getTypesDWColumns.put(24, NVARCHAR); // SS_UDT_SCHEMA_NAME + getTypesDWColumns.put(25, NVARCHAR); // SS_UDT_ASSEMBLY_TYPE_NAME + getTypesDWColumns.put(26, NVARCHAR); // SS_XML_SCHEMACOLLECTION_CATALOG_NAME + getTypesDWColumns.put(27, NVARCHAR); // SS_XML_SCHEMACOLLECTION_SCHEMA_NAME + getTypesDWColumns.put(28, NVARCHAR); // SS_XML_SCHEMACOLLECTION_NAME + } + + // Ensure there is a data type for every metadata column + assert(getColumnsDWColumns.size() == getTypesDWColumns.size()); } finally { LOCK.unlock(); } @@ -744,7 +795,7 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table if (!isFirstRow) { azureDwSelectBuilder.append(" UNION ALL "); } - azureDwSelectBuilder.append(generateAzureDWSelect(rs, getColumnsDWColumns)); + azureDwSelectBuilder.append(generateAzureDWSelect(rs, getColumnsDWColumns, getTypesDWColumns)); isFirstRow = false; } @@ -780,33 +831,34 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table } } - private String generateAzureDWSelect(ResultSet rs, Map columns) throws SQLException { + private String generateAzureDWSelect(ResultSet rs, Map columns, Map types) throws SQLException { StringBuilder sb = new StringBuilder("SELECT "); for (Entry p : columns.entrySet()) { + sb.append("CAST("); if (p.getKey() < 0) { - sb.append("NULL"); + sb.append("NULL AS " + types.get(p.getKey())); } else { Object o = rs.getObject(p.getKey()); if (null == o) { - sb.append("NULL"); + sb.append("NULL AS " + types.get(p.getKey())); } else if (o instanceof Number) { if (IS_AUTOINCREMENT.equalsIgnoreCase(p.getValue()) || IS_GENERATEDCOLUMN.equalsIgnoreCase(p.getValue())) { sb.append("'").append(Util.escapeSingleQuotes(Util.zeroOneToYesNo(((Number) o).intValue()))) - .append("'"); + .append("' AS ").append(types.get(p.getKey())); } else { - sb.append(o.toString()); + sb.append(o.toString()).append(" AS ").append(types.get(p.getKey())); } } else { - sb.append("'").append(Util.escapeSingleQuotes(o.toString())).append("'"); + sb.append("'").append(Util.escapeSingleQuotes(o.toString())).append("' AS ").append(types.get(p.getKey())).append("(").append(Integer.toString(o.toString().length())).append(")"); } } - sb.append(" AS ").append(p.getValue()).append(","); + sb.append(") AS ").append(p.getValue()).append(","); } sb.setLength(sb.length() - 1); return sb.toString(); } - + private String generateAzureDWEmptyRS(Map columns) { StringBuilder sb = new StringBuilder("SELECT TOP 0 "); for (Entry p : columns.entrySet()) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java index ff2f2fc73..7bea27444 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -63,6 +63,39 @@ public class DatabaseMetaDataTest extends AbstractTest { private static final String functionName = RandomUtil.getIdentifier("DBMetadataFunction"); private static Map getColumnsDWColumns = null; private static Map getImportedKeysDWColumns = null; + private static Map getColumnMetaDataType = null; + private static final int TABLE_CAT = 1; + private static final int TABLE_SCHEM = 2; + private static final int TABLE_NAME = 3; + private static final int COLUMN_NAME = 4; + private static final int DATA_TYPE = 5; + private static final int TYPE_NAME = 6; + private static final int COLUMN_SIZE = 7; + private static final int BUFFER_LENGTH = 8; + private static final int DECIMAL_DIGITS = 9; + private static final int NUM_PREC_RADIX = 10; + private static final int NULLABLE = 11; + private static final int REMARKS = 12; + private static final int COLUMN_DEF = 13; + private static final int SQL_DATA_TYPE = 14; + private static final int SQL_DATETIME_SUB = 15; + private static final int CHAR_OCTET_LENGTH = 16; + private static final int ORDINAL_POSITION = 17; + private static final int IS_NULLABLE = 18; + private static final int SCOPE_CATALOG = 19; + private static final int SCOPE_SCHEMA = 20; + private static final int SCOPE_TABLE = 21; + private static final int SOURCE_DATA_TYPE = 22; + private static final int IS_AUTOINCREMENT = 23; + private static final int IS_GENERATEDCOLUMN = 24; + private static final int SS_IS_SPARSE = 25; + private static final int SS_IS_COLUMN_SET = 26; + private static final int SS_UDT_CATALOG_NAME = 27; + private static final int SS_UDT_SCHEMA_NAME = 28; + private static final int SS_UDT_ASSEMBLY_TYPE_NAME = 29; + private static final int SS_XML_SCHEMACOLLECTION_CATALOG_NAME = 30; + private static final int SS_XML_SCHEMACOLLECTION_SCHEMA_NAME = 31; + private static final int SS_XML_SCHEMACOLLECTION_NAME = 32; /** * Verify DatabaseMetaData#isWrapperFor and DatabaseMetaData#unwrap. @@ -887,6 +920,57 @@ public void testGetImportedKeysDW() throws SQLException { } } + @Test + public void testValidateColumnMetadata() throws SQLException { + + if (getColumnMetaDataType == null) { + getColumnMetaDataType = new LinkedHashMap<>(); + getColumnMetaDataType.put(TABLE_CAT, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(TABLE_SCHEM, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(TABLE_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(COLUMN_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(DATA_TYPE, java.sql.Types.INTEGER); + getColumnMetaDataType.put(TYPE_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(COLUMN_SIZE, java.sql.Types.INTEGER); + getColumnMetaDataType.put(BUFFER_LENGTH, java.sql.Types.INTEGER); + getColumnMetaDataType.put(DECIMAL_DIGITS, java.sql.Types.INTEGER); + getColumnMetaDataType.put(NUM_PREC_RADIX, java.sql.Types.INTEGER); + getColumnMetaDataType.put(NULLABLE, java.sql.Types.INTEGER); + getColumnMetaDataType.put(REMARKS, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(COLUMN_DEF, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SQL_DATA_TYPE, java.sql.Types.INTEGER); + getColumnMetaDataType.put(SQL_DATETIME_SUB, java.sql.Types.INTEGER); + getColumnMetaDataType.put(CHAR_OCTET_LENGTH, java.sql.Types.INTEGER); + getColumnMetaDataType.put(ORDINAL_POSITION, java.sql.Types.INTEGER); + getColumnMetaDataType.put(IS_NULLABLE, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SCOPE_CATALOG, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SCOPE_SCHEMA, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SCOPE_TABLE, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SOURCE_DATA_TYPE, java.sql.Types.SMALLINT); + getColumnMetaDataType.put(IS_AUTOINCREMENT, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(IS_GENERATEDCOLUMN, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SS_IS_SPARSE, java.sql.Types.SMALLINT); + getColumnMetaDataType.put(SS_IS_COLUMN_SET, java.sql.Types.SMALLINT); + getColumnMetaDataType.put(SS_UDT_CATALOG_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_UDT_SCHEMA_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_UDT_ASSEMBLY_TYPE_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_CATALOG_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_NAME, java.sql.Types.NVARCHAR); + } + + try (Connection conn = getConnection();) { + ResultSetMetaData metadata = conn.getMetaData().getColumns(null, null, tableName, null).getMetaData(); + + // Ensure that there is a data type for every metadata column + assert(metadata.getColumnCount() == getColumnMetaDataType.size()); + + for (int i = 1; i < metadata.getColumnCount(); i++) { + assertEquals(getColumnMetaDataType.get(i), metadata.getColumnType(i)); + } + } + } + @BeforeAll public static void setupTable() throws Exception { setConnection(); From c95f6f3438d9d578909af9276d54874072776e4b Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Fri, 9 Feb 2024 15:54:16 -0800 Subject: [PATCH 02/10] Fix to ensure metadata returned follows JDBC data type specs --- .../jdbc/SQLServerDatabaseMetaData.java | 94 +++++++++---------- .../DatabaseMetaDataTest.java | 74 +++++++-------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 6af0b2f4a..f61032ff8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -636,18 +636,18 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table + "INSERT INTO @mssqljdbc_temp_sp_columns_result EXEC sp_columns_100 ?,?,?,?,?,?;" - + "SELECT TABLE_QUALIFIER AS TABLE_CAT, TABLE_OWNER AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, " - + "CAST (DATA_TYPE AS INT) AS DATA_TYPE,TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, " - + "CAST(SCALE AS INT) AS DECIMAL_DIGITS, CAST(RADIX AS INT) AS NUM_PREC_RADIX,CAST(NULLABLE AS INT) AS NULLABLE, " - + "CAST(REMARKS AS VARCHAR) AS REMARKS, COLUMN_DEF, CAST(SQL_DATA_TYPE AS INT) AS SQL_DATA_TYPE, " - + "CAST(SQL_DATETIME_SUB AS INT) AS SQL_DATETIME_SUB, CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE," - + "CAST(NULL AS VARCHAR) AS SCOPE_CATALOG, CAST(NULL AS VARCHAR) AS SCOPE_SCHEMA, CAST(NULL AS VARCHAR) AS SCOPE_TABLE, " - + "CAST(SS_DATA_TYPE AS SMALLINT) AS SOURCE_DATA_TYPE, " - + "CASE SS_IS_IDENTITY WHEN 0 THEN 'NO' WHEN 1 THEN 'YES' WHEN '' THEN '' END AS IS_AUTOINCREMENT," - + "CASE SS_IS_COMPUTED WHEN 0 THEN 'NO' WHEN 1 THEN 'YES' WHEN '' THEN '' END AS IS_GENERATEDCOLUMN, " - + "SS_IS_SPARSE, SS_IS_COLUMN_SET, SS_UDT_CATALOG_NAME, SS_UDT_SCHEMA_NAME, SS_UDT_ASSEMBLY_TYPE_NAME," - + "SS_XML_SCHEMACOLLECTION_CATALOG_NAME, SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, SS_XML_SCHEMACOLLECTION_NAME " - + "FROM @mssqljdbc_temp_sp_columns_result ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION;"; + + "SELECT TABLE_QUALIFIER AS TABLE_CAT, TABLE_OWNER AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, " + + "CAST (DATA_TYPE AS INT) AS DATA_TYPE,TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, " + + "CAST(SCALE AS INT) AS DECIMAL_DIGITS, CAST(RADIX AS INT) AS NUM_PREC_RADIX,CAST(NULLABLE AS INT) AS NULLABLE, " + + "CAST(REMARKS AS VARCHAR) AS REMARKS, COLUMN_DEF, CAST(SQL_DATA_TYPE AS INT) AS SQL_DATA_TYPE, " + + "CAST(SQL_DATETIME_SUB AS INT) AS SQL_DATETIME_SUB, CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE," + + "CAST(NULL AS VARCHAR) AS SCOPE_CATALOG, CAST(NULL AS VARCHAR) AS SCOPE_SCHEMA, CAST(NULL AS VARCHAR) AS SCOPE_TABLE, " + + "CAST(SS_DATA_TYPE AS SMALLINT) AS SOURCE_DATA_TYPE, " + + "CASE SS_IS_IDENTITY WHEN 0 THEN 'NO' WHEN 1 THEN 'YES' WHEN '' THEN '' END AS IS_AUTOINCREMENT," + + "CASE SS_IS_COMPUTED WHEN 0 THEN 'NO' WHEN 1 THEN 'YES' WHEN '' THEN '' END AS IS_GENERATEDCOLUMN, " + + "SS_IS_SPARSE, SS_IS_COLUMN_SET, SS_UDT_CATALOG_NAME, SS_UDT_SCHEMA_NAME, SS_UDT_ASSEMBLY_TYPE_NAME," + + "SS_XML_SCHEMACOLLECTION_CATALOG_NAME, SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, SS_XML_SCHEMACOLLECTION_NAME " + + "FROM @mssqljdbc_temp_sp_columns_result ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION;"; SQLServerResultSet rs = null; PreparedStatement pstmt = (SQLServerPreparedStatement) this.connection.prepareStatement(spColumnsSql); pstmt.closeOnCompletion(); @@ -731,43 +731,43 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table getColumnsDWColumns.put(28, SS_XML_SCHEMACOLLECTION_NAME); } if (null == getTypesDWColumns) { - getTypesDWColumns = new LinkedHashMap<>(); - getTypesDWColumns.put(1, NVARCHAR); // TABLE_CAT - getTypesDWColumns.put(2, NVARCHAR); // TABLE_SCHEM - getTypesDWColumns.put(3, NVARCHAR); // TABLE_NAME - getTypesDWColumns.put(4, NVARCHAR); // COLUMN_NAME - getTypesDWColumns.put(5, INTEGER); // DATA_TYPE - getTypesDWColumns.put(6, NVARCHAR); // TYPE_NAME - getTypesDWColumns.put(7, INTEGER); // COLUMN_SIZE - getTypesDWColumns.put(8, INTEGER); // BUFFER_LENGTH - getTypesDWColumns.put(9, INTEGER); // DECIMAL_DIGITS - getTypesDWColumns.put(10, INTEGER); // NUM_PREC_RADIX - getTypesDWColumns.put(11, INTEGER); // NULLABLE - getTypesDWColumns.put(12, VARCHAR); // REMARKS - getTypesDWColumns.put(13, NVARCHAR); // COLUMN_DEF - getTypesDWColumns.put(14, INTEGER); // SQL_DATA_TYPE - getTypesDWColumns.put(15, INTEGER); // SQL_DATETIME_SUB - getTypesDWColumns.put(16, INTEGER); // CHAR_OCTET_LENGTH - getTypesDWColumns.put(17, INTEGER); // ORDINAL_POSITION - getTypesDWColumns.put(18, VARCHAR); // IS_NULLABLE + getTypesDWColumns = new LinkedHashMap<>(); + getTypesDWColumns.put(1, NVARCHAR); // TABLE_CAT + getTypesDWColumns.put(2, NVARCHAR); // TABLE_SCHEM + getTypesDWColumns.put(3, NVARCHAR); // TABLE_NAME + getTypesDWColumns.put(4, NVARCHAR); // COLUMN_NAME + getTypesDWColumns.put(5, INTEGER); // DATA_TYPE + getTypesDWColumns.put(6, NVARCHAR); // TYPE_NAME + getTypesDWColumns.put(7, INTEGER); // COLUMN_SIZE + getTypesDWColumns.put(8, INTEGER); // BUFFER_LENGTH + getTypesDWColumns.put(9, INTEGER); // DECIMAL_DIGITS + getTypesDWColumns.put(10, INTEGER); // NUM_PREC_RADIX + getTypesDWColumns.put(11, INTEGER); // NULLABLE + getTypesDWColumns.put(12, VARCHAR); // REMARKS + getTypesDWColumns.put(13, NVARCHAR); // COLUMN_DEF + getTypesDWColumns.put(14, INTEGER); // SQL_DATA_TYPE + getTypesDWColumns.put(15, INTEGER); // SQL_DATETIME_SUB + getTypesDWColumns.put(16, INTEGER); // CHAR_OCTET_LENGTH + getTypesDWColumns.put(17, INTEGER); // ORDINAL_POSITION + getTypesDWColumns.put(18, VARCHAR); // IS_NULLABLE /* * Use negative value keys to indicate that this column doesn't exist in SQL Server and should just * be queried as 'NULL' */ - getTypesDWColumns.put(-1, VARCHAR); // SCOPE_CATALOG - getTypesDWColumns.put(-2, VARCHAR); // SCOPE_SCHEMA - getTypesDWColumns.put(-3, VARCHAR); // SCOPE_TABLE - getTypesDWColumns.put(29, SMALLINT); // SOURCE_DATA_TYPE - getTypesDWColumns.put(22, VARCHAR); // IS_AUTOINCREMENT - getTypesDWColumns.put(21, VARCHAR); // IS_GENERATEDCOLUMN - getTypesDWColumns.put(19, SMALLINT); // SS_IS_SPARSE - getTypesDWColumns.put(20, SMALLINT); // SS_IS_COLUMN_SET - getTypesDWColumns.put(23, NVARCHAR); // SS_UDT_CATALOG_NAME - getTypesDWColumns.put(24, NVARCHAR); // SS_UDT_SCHEMA_NAME - getTypesDWColumns.put(25, NVARCHAR); // SS_UDT_ASSEMBLY_TYPE_NAME - getTypesDWColumns.put(26, NVARCHAR); // SS_XML_SCHEMACOLLECTION_CATALOG_NAME - getTypesDWColumns.put(27, NVARCHAR); // SS_XML_SCHEMACOLLECTION_SCHEMA_NAME - getTypesDWColumns.put(28, NVARCHAR); // SS_XML_SCHEMACOLLECTION_NAME + getTypesDWColumns.put(-1, VARCHAR); // SCOPE_CATALOG + getTypesDWColumns.put(-2, VARCHAR); // SCOPE_SCHEMA + getTypesDWColumns.put(-3, VARCHAR); // SCOPE_TABLE + getTypesDWColumns.put(29, SMALLINT); // SOURCE_DATA_TYPE + getTypesDWColumns.put(22, VARCHAR); // IS_AUTOINCREMENT + getTypesDWColumns.put(21, VARCHAR); // IS_GENERATEDCOLUMN + getTypesDWColumns.put(19, SMALLINT); // SS_IS_SPARSE + getTypesDWColumns.put(20, SMALLINT); // SS_IS_COLUMN_SET + getTypesDWColumns.put(23, NVARCHAR); // SS_UDT_CATALOG_NAME + getTypesDWColumns.put(24, NVARCHAR); // SS_UDT_SCHEMA_NAME + getTypesDWColumns.put(25, NVARCHAR); // SS_UDT_ASSEMBLY_TYPE_NAME + getTypesDWColumns.put(26, NVARCHAR); // SS_XML_SCHEMACOLLECTION_CATALOG_NAME + getTypesDWColumns.put(27, NVARCHAR); // SS_XML_SCHEMACOLLECTION_SCHEMA_NAME + getTypesDWColumns.put(28, NVARCHAR); // SS_XML_SCHEMACOLLECTION_NAME } // Ensure there is a data type for every metadata column @@ -834,13 +834,13 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table private String generateAzureDWSelect(ResultSet rs, Map columns, Map types) throws SQLException { StringBuilder sb = new StringBuilder("SELECT "); for (Entry p : columns.entrySet()) { - sb.append("CAST("); + sb.append("CAST("); if (p.getKey() < 0) { sb.append("NULL AS " + types.get(p.getKey())); } else { Object o = rs.getObject(p.getKey()); if (null == o) { - sb.append("NULL AS " + types.get(p.getKey())); + sb.append("NULL AS " + types.get(p.getKey())); } else if (o instanceof Number) { if (IS_AUTOINCREMENT.equalsIgnoreCase(p.getValue()) || IS_GENERATEDCOLUMN.equalsIgnoreCase(p.getValue())) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java index 7bea27444..357bd71ae 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -924,47 +924,47 @@ public void testGetImportedKeysDW() throws SQLException { public void testValidateColumnMetadata() throws SQLException { if (getColumnMetaDataType == null) { - getColumnMetaDataType = new LinkedHashMap<>(); - getColumnMetaDataType.put(TABLE_CAT, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(TABLE_SCHEM, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(TABLE_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(COLUMN_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(DATA_TYPE, java.sql.Types.INTEGER); - getColumnMetaDataType.put(TYPE_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(COLUMN_SIZE, java.sql.Types.INTEGER); - getColumnMetaDataType.put(BUFFER_LENGTH, java.sql.Types.INTEGER); - getColumnMetaDataType.put(DECIMAL_DIGITS, java.sql.Types.INTEGER); - getColumnMetaDataType.put(NUM_PREC_RADIX, java.sql.Types.INTEGER); - getColumnMetaDataType.put(NULLABLE, java.sql.Types.INTEGER); - getColumnMetaDataType.put(REMARKS, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(COLUMN_DEF, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SQL_DATA_TYPE, java.sql.Types.INTEGER); - getColumnMetaDataType.put(SQL_DATETIME_SUB, java.sql.Types.INTEGER); - getColumnMetaDataType.put(CHAR_OCTET_LENGTH, java.sql.Types.INTEGER); - getColumnMetaDataType.put(ORDINAL_POSITION, java.sql.Types.INTEGER); - getColumnMetaDataType.put(IS_NULLABLE, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SCOPE_CATALOG, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SCOPE_SCHEMA, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SCOPE_TABLE, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SOURCE_DATA_TYPE, java.sql.Types.SMALLINT); - getColumnMetaDataType.put(IS_AUTOINCREMENT, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(IS_GENERATEDCOLUMN, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SS_IS_SPARSE, java.sql.Types.SMALLINT); - getColumnMetaDataType.put(SS_IS_COLUMN_SET, java.sql.Types.SMALLINT); - getColumnMetaDataType.put(SS_UDT_CATALOG_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_UDT_SCHEMA_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_UDT_ASSEMBLY_TYPE_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_CATALOG_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType = new LinkedHashMap<>(); + getColumnMetaDataType.put(TABLE_CAT, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(TABLE_SCHEM, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(TABLE_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(COLUMN_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(DATA_TYPE, java.sql.Types.INTEGER); + getColumnMetaDataType.put(TYPE_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(COLUMN_SIZE, java.sql.Types.INTEGER); + getColumnMetaDataType.put(BUFFER_LENGTH, java.sql.Types.INTEGER); + getColumnMetaDataType.put(DECIMAL_DIGITS, java.sql.Types.INTEGER); + getColumnMetaDataType.put(NUM_PREC_RADIX, java.sql.Types.INTEGER); + getColumnMetaDataType.put(NULLABLE, java.sql.Types.INTEGER); + getColumnMetaDataType.put(REMARKS, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(COLUMN_DEF, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SQL_DATA_TYPE, java.sql.Types.INTEGER); + getColumnMetaDataType.put(SQL_DATETIME_SUB, java.sql.Types.INTEGER); + getColumnMetaDataType.put(CHAR_OCTET_LENGTH, java.sql.Types.INTEGER); + getColumnMetaDataType.put(ORDINAL_POSITION, java.sql.Types.INTEGER); + getColumnMetaDataType.put(IS_NULLABLE, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SCOPE_CATALOG, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SCOPE_SCHEMA, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SCOPE_TABLE, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SOURCE_DATA_TYPE, java.sql.Types.SMALLINT); + getColumnMetaDataType.put(IS_AUTOINCREMENT, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(IS_GENERATEDCOLUMN, java.sql.Types.VARCHAR); + getColumnMetaDataType.put(SS_IS_SPARSE, java.sql.Types.SMALLINT); + getColumnMetaDataType.put(SS_IS_COLUMN_SET, java.sql.Types.SMALLINT); + getColumnMetaDataType.put(SS_UDT_CATALOG_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_UDT_SCHEMA_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_UDT_ASSEMBLY_TYPE_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_CATALOG_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, java.sql.Types.NVARCHAR); + getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_NAME, java.sql.Types.NVARCHAR); } try (Connection conn = getConnection();) { - ResultSetMetaData metadata = conn.getMetaData().getColumns(null, null, tableName, null).getMetaData(); + ResultSetMetaData metadata = conn.getMetaData().getColumns(null, null, tableName, null).getMetaData(); + + // Ensure that there is a data type for every metadata column + assert(metadata.getColumnCount() == getColumnMetaDataType.size()); - // Ensure that there is a data type for every metadata column - assert(metadata.getColumnCount() == getColumnMetaDataType.size()); - for (int i = 1; i < metadata.getColumnCount(); i++) { assertEquals(getColumnMetaDataType.get(i), metadata.getColumnType(i)); } From 552cccb398f63c833adb075fd89a1fcfc7a02eaa Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Wed, 21 Feb 2024 12:41:27 -0800 Subject: [PATCH 03/10] Changes requested in code review --- .../jdbc/SQLServerDatabaseMetaData.java | 16 +- .../sqlserver/jdbc/SQLServerResource.java | 3 +- .../sqlserver/jdbc/TestResource.java | 4 +- .../DatabaseMetaDataTest.java | 171 ++++++++++-------- 4 files changed, 115 insertions(+), 79 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index f61032ff8..fe87824b8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -25,6 +25,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; +import com.microsoft.sqlserver.jdbc.JDBCType; /** @@ -258,10 +259,10 @@ private void checkClosed() throws SQLServerException { private static final String IS_AUTOINCREMENT = "IS_AUTOINCREMENT"; private static final String ACTIVITY_ID = " ActivityId: "; - private static final String NVARCHAR = "NVARCHAR"; - private static final String VARCHAR = "VARCHAR"; - private static final String INTEGER = "INT"; - private static final String SMALLINT = "SMALLINT"; + private static final String NVARCHAR = JDBCType.NVARCHAR.name(); + private static final String VARCHAR = JDBCType.VARCHAR.name(); + private static final String INTEGER = JDBCType.INTEGER.name(); + private static final String SMALLINT = JDBCType.SMALLINT.name(); private static final String SQL_KEYWORDS = createSqlKeyWords(); @@ -771,7 +772,12 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table } // Ensure there is a data type for every metadata column - assert(getColumnsDWColumns.size() == getTypesDWColumns.size()); + if (getColumnsDWColumns.size() != getTypesDWColumns.size()) { + MessageFormat form = new MessageFormat( + SQLServerException.getErrString("R_colCountNotMatchColTypeCount")); + Object[] msgArgs = {getColumnsDWColumns.size(), getTypesDWColumns.size()}; + throw new IllegalArgumentException(form.format(msgArgs)); + } } finally { LOCK.unlock(); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 95b5ffa0b..6cd502b72 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -545,7 +545,8 @@ protected Object[][] getContents() { {"R_ManagedIdentityTokenAcquisitionFail", "Failed to acquire managed identity token. Request for the token succeeded, but no token was returned. The token is null."}, {"R_AmbiguousRowUpdate", "Failed to execute updateRow(). The update is attempting an ambiguous update on tables \"{0}\" and \"{1}\". Ensure all columns being updated prior to the updateRow() call belong to the same table."}, {"R_InvalidSqlQuery", "Invalid SQL Query: {0}"}, - {"R_InvalidScale", "Scale of input value is larger than the maximum allowed by SQL Server."} + {"R_InvalidScale", "Scale of input value is larger than the maximum allowed by SQL Server."}, + {"R_colCountNotMatchColTypeCount", "Number of provided columns {0} does not match the column data types definition {1}."}, }; } // @formatter:on diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index e14bb671a..879e83a5b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -212,5 +212,7 @@ protected Object[][] getContents() { {"R_failedFedauth", "Failed to acquire fedauth token: "}, {"R_noLoginModulesConfiguredForJdbcDriver", "javax.security.auth.login.LoginException (No LoginModules configured for SQLJDBCDriver)"}, - {"R_unexpectedThreadCount", "Thread count is higher than expected."}}; + {"R_unexpectedThreadCount", "Thread count is higher than expected."}, + {"R_classLoaderNotFoundForColumnType", "Class Loader for type {0} not found for column {1}"}, + {"R_classNotAssignable", "Class {0} is not assignable from class {1} for column {2}"}}; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java index 357bd71ae..73bf060d8 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -63,39 +63,39 @@ public class DatabaseMetaDataTest extends AbstractTest { private static final String functionName = RandomUtil.getIdentifier("DBMetadataFunction"); private static Map getColumnsDWColumns = null; private static Map getImportedKeysDWColumns = null; - private static Map getColumnMetaDataType = null; - private static final int TABLE_CAT = 1; - private static final int TABLE_SCHEM = 2; - private static final int TABLE_NAME = 3; - private static final int COLUMN_NAME = 4; - private static final int DATA_TYPE = 5; - private static final int TYPE_NAME = 6; - private static final int COLUMN_SIZE = 7; - private static final int BUFFER_LENGTH = 8; - private static final int DECIMAL_DIGITS = 9; - private static final int NUM_PREC_RADIX = 10; - private static final int NULLABLE = 11; - private static final int REMARKS = 12; - private static final int COLUMN_DEF = 13; - private static final int SQL_DATA_TYPE = 14; - private static final int SQL_DATETIME_SUB = 15; - private static final int CHAR_OCTET_LENGTH = 16; - private static final int ORDINAL_POSITION = 17; - private static final int IS_NULLABLE = 18; - private static final int SCOPE_CATALOG = 19; - private static final int SCOPE_SCHEMA = 20; - private static final int SCOPE_TABLE = 21; - private static final int SOURCE_DATA_TYPE = 22; - private static final int IS_AUTOINCREMENT = 23; - private static final int IS_GENERATEDCOLUMN = 24; - private static final int SS_IS_SPARSE = 25; - private static final int SS_IS_COLUMN_SET = 26; - private static final int SS_UDT_CATALOG_NAME = 27; - private static final int SS_UDT_SCHEMA_NAME = 28; - private static final int SS_UDT_ASSEMBLY_TYPE_NAME = 29; - private static final int SS_XML_SCHEMACOLLECTION_CATALOG_NAME = 30; - private static final int SS_XML_SCHEMACOLLECTION_SCHEMA_NAME = 31; - private static final int SS_XML_SCHEMACOLLECTION_NAME = 32; + private static Map> getColumnMetaDataClass = null; + private static final String TABLE_CAT = "TABLE_CAT"; + private static final String TABLE_SCHEM = "TABLE_SCHEM"; + private static final String TABLE_NAME = "TABLE_NAME"; + private static final String COLUMN_NAME = "COLUMN_NAME"; + private static final String DATA_TYPE = "DATA_TYPE"; + private static final String TYPE_NAME = "TYPE_NAME"; + private static final String COLUMN_SIZE = "COLUMN_SIZE"; + private static final String BUFFER_LENGTH = "BUFFER_LENGTH"; + private static final String DECIMAL_DIGITS = "DECIMAL_DIGITS"; + private static final String NUM_PREC_RADIX = "NUM_PREC_RADIX"; + private static final String NULLABLE = "NULLABLE"; + private static final String REMARKS = "REMARKS"; + private static final String COLUMN_DEF = "COLUMN_DEF"; + private static final String SQL_DATA_TYPE = "SQL_DATA_TYPE"; + private static final String SQL_DATETIME_SUB = "SQL_DATETIME_SUB"; + private static final String CHAR_OCTET_LENGTH = "CHAR_OCTET_LENGTH"; + private static final String ORDINAL_POSITION = "ORDINAL_POSITION"; + private static final String IS_NULLABLE = "IS_NULLABLE"; + private static final String SCOPE_CATALOG = "SCOPE_CATALOG"; + private static final String SCOPE_SCHEMA = "SCOPE_SCHEMA"; + private static final String SCOPE_TABLE = "SCOPE_TABLE"; + private static final String SOURCE_DATA_TYPE = "SOURCE_DATA_TYPE"; + private static final String IS_AUTOINCREMENT = "IS_AUTOINCREMENT"; + private static final String IS_GENERATEDCOLUMN = "IS_GENERATEDCOLUMN"; + private static final String SS_IS_SPARSE = "SS_IS_SPARSE"; + private static final String SS_IS_COLUMN_SET = "SS_IS_COLUMN_SET"; + private static final String SS_UDT_CATALOG_NAME = "SS_UDT_CATALOG_NAME"; + private static final String SS_UDT_SCHEMA_NAME = "SS_UDT_SCHEMA_NAME"; + private static final String SS_UDT_ASSEMBLY_TYPE_NAME = "SS_UDT_ASSEMBLY_TYPE_NAME"; + private static final String SS_XML_SCHEMACOLLECTION_CATALOG_NAME = "SS_XML_SCHEMACOLLECTION_CATALOG_NAME"; + private static final String SS_XML_SCHEMACOLLECTION_SCHEMA_NAME = "SS_XML_SCHEMACOLLECTION_SCHEMA_NAME"; + private static final String SS_XML_SCHEMACOLLECTION_NAME = "SS_XML_SCHEMACOLLECTION_NAME"; /** * Verify DatabaseMetaData#isWrapperFor and DatabaseMetaData#unwrap. @@ -921,53 +921,80 @@ public void testGetImportedKeysDW() throws SQLException { } @Test + // Validates the metadata data types defined by JDBC spec + // https://docs.oracle.com/javase/8/docs/api/java/sql/DatabaseMetaData.html#getColumns-java.lang.String-java.lang.String-java.lang.String-java.lang.String- public void testValidateColumnMetadata() throws SQLException { - if (getColumnMetaDataType == null) { - getColumnMetaDataType = new LinkedHashMap<>(); - getColumnMetaDataType.put(TABLE_CAT, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(TABLE_SCHEM, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(TABLE_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(COLUMN_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(DATA_TYPE, java.sql.Types.INTEGER); - getColumnMetaDataType.put(TYPE_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(COLUMN_SIZE, java.sql.Types.INTEGER); - getColumnMetaDataType.put(BUFFER_LENGTH, java.sql.Types.INTEGER); - getColumnMetaDataType.put(DECIMAL_DIGITS, java.sql.Types.INTEGER); - getColumnMetaDataType.put(NUM_PREC_RADIX, java.sql.Types.INTEGER); - getColumnMetaDataType.put(NULLABLE, java.sql.Types.INTEGER); - getColumnMetaDataType.put(REMARKS, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(COLUMN_DEF, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SQL_DATA_TYPE, java.sql.Types.INTEGER); - getColumnMetaDataType.put(SQL_DATETIME_SUB, java.sql.Types.INTEGER); - getColumnMetaDataType.put(CHAR_OCTET_LENGTH, java.sql.Types.INTEGER); - getColumnMetaDataType.put(ORDINAL_POSITION, java.sql.Types.INTEGER); - getColumnMetaDataType.put(IS_NULLABLE, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SCOPE_CATALOG, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SCOPE_SCHEMA, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SCOPE_TABLE, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SOURCE_DATA_TYPE, java.sql.Types.SMALLINT); - getColumnMetaDataType.put(IS_AUTOINCREMENT, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(IS_GENERATEDCOLUMN, java.sql.Types.VARCHAR); - getColumnMetaDataType.put(SS_IS_SPARSE, java.sql.Types.SMALLINT); - getColumnMetaDataType.put(SS_IS_COLUMN_SET, java.sql.Types.SMALLINT); - getColumnMetaDataType.put(SS_UDT_CATALOG_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_UDT_SCHEMA_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_UDT_ASSEMBLY_TYPE_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_CATALOG_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, java.sql.Types.NVARCHAR); - getColumnMetaDataType.put(SS_XML_SCHEMACOLLECTION_NAME, java.sql.Types.NVARCHAR); + if (getColumnMetaDataClass == null) { + getColumnMetaDataClass = new LinkedHashMap<>(); + getColumnMetaDataClass.put(TABLE_CAT, String.class); + getColumnMetaDataClass.put(TABLE_SCHEM, String.class); + getColumnMetaDataClass.put(TABLE_NAME, String.class); + getColumnMetaDataClass.put(COLUMN_NAME, String.class); + getColumnMetaDataClass.put(DATA_TYPE, Integer.class); + getColumnMetaDataClass.put(TYPE_NAME, String.class); + getColumnMetaDataClass.put(COLUMN_SIZE, Integer.class); + getColumnMetaDataClass.put(BUFFER_LENGTH, Object.class); // Not used + getColumnMetaDataClass.put(DECIMAL_DIGITS, Integer.class); + getColumnMetaDataClass.put(NUM_PREC_RADIX, Integer.class); + getColumnMetaDataClass.put(NULLABLE, Integer.class); + getColumnMetaDataClass.put(REMARKS, String.class); + getColumnMetaDataClass.put(COLUMN_DEF, String.class); + getColumnMetaDataClass.put(SQL_DATA_TYPE, Integer.class); + getColumnMetaDataClass.put(SQL_DATETIME_SUB, Integer.class); + getColumnMetaDataClass.put(CHAR_OCTET_LENGTH, Integer.class); + getColumnMetaDataClass.put(ORDINAL_POSITION, Integer.class); + getColumnMetaDataClass.put(IS_NULLABLE, String.class); + getColumnMetaDataClass.put(SCOPE_CATALOG, String.class); + getColumnMetaDataClass.put(SCOPE_SCHEMA, String.class); + getColumnMetaDataClass.put(SCOPE_TABLE, String.class); + getColumnMetaDataClass.put(SOURCE_DATA_TYPE, Short.class); + getColumnMetaDataClass.put(IS_AUTOINCREMENT, String.class); + getColumnMetaDataClass.put(IS_GENERATEDCOLUMN, String.class); + getColumnMetaDataClass.put(SS_IS_SPARSE, Short.class); + getColumnMetaDataClass.put(SS_IS_COLUMN_SET, Short.class); + getColumnMetaDataClass.put(SS_UDT_CATALOG_NAME, String.class); + getColumnMetaDataClass.put(SS_UDT_SCHEMA_NAME, String.class); + getColumnMetaDataClass.put(SS_UDT_ASSEMBLY_TYPE_NAME, String.class); + getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_CATALOG_NAME, String.class); + getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, String.class); + getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_NAME, String.class); } - try (Connection conn = getConnection();) { + try (Connection conn = getConnection()) { ResultSetMetaData metadata = conn.getMetaData().getColumns(null, null, tableName, null).getMetaData(); - // Ensure that there is a data type for every metadata column - assert(metadata.getColumnCount() == getColumnMetaDataType.size()); - for (int i = 1; i < metadata.getColumnCount(); i++) { - assertEquals(getColumnMetaDataType.get(i), metadata.getColumnType(i)); + // Ensure that there is a data type for every metadata column + String columnLabel = metadata.getColumnLabel(i); + String columnClassName = metadata.getColumnClassName(i); + Class columnClass = null; + + try + { + columnClass = Class.forName(columnClassName); + } + catch (ClassNotFoundException ex) + { + MessageFormat form = new MessageFormat( + TestResource.getResource("R_classLoaderNotFoundForColumnType")); + Object[] msgArgs = {columnClassName, columnLabel}; + fail(form.format(msgArgs)); + } + + Class expectedClass = getColumnMetaDataClass.get(columnLabel); + + assert(expectedClass != null); + + if (!expectedClass.isAssignableFrom(columnClass)) { + MessageFormat form = new MessageFormat( + TestResource.getResource("R_classNotAssignable")); + Object[] msgArgs = {expectedClass.getName(), columnClassName, columnLabel}; + fail(form.format(msgArgs)); + } } + } catch (SQLException e) { + fail(e.getMessage()); } } From 0b95bc22b55811c79a67b6ebaa62e0045f43255f Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Tue, 27 Feb 2024 10:00:50 -0800 Subject: [PATCH 04/10] Updates requested in the last code review --- .../jdbc/SQLServerDatabaseMetaData.java | 94 +++++++++++-------- .../DatabaseMetaDataTest.java | 45 +++------ 2 files changed, 67 insertions(+), 72 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index fe87824b8..67f120ec0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -258,7 +258,7 @@ private void checkClosed() throws SQLServerException { private static final String IS_GENERATEDCOLUMN = "IS_GENERATEDCOLUMN"; private static final String IS_AUTOINCREMENT = "IS_AUTOINCREMENT"; private static final String ACTIVITY_ID = " ActivityId: "; - + private static final String NVARCHAR = JDBCType.NVARCHAR.name(); private static final String VARCHAR = JDBCType.VARCHAR.name(); private static final String INTEGER = JDBCType.INTEGER.name(); @@ -733,44 +733,44 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table } if (null == getTypesDWColumns) { getTypesDWColumns = new LinkedHashMap<>(); - getTypesDWColumns.put(1, NVARCHAR); // TABLE_CAT - getTypesDWColumns.put(2, NVARCHAR); // TABLE_SCHEM - getTypesDWColumns.put(3, NVARCHAR); // TABLE_NAME - getTypesDWColumns.put(4, NVARCHAR); // COLUMN_NAME - getTypesDWColumns.put(5, INTEGER); // DATA_TYPE - getTypesDWColumns.put(6, NVARCHAR); // TYPE_NAME - getTypesDWColumns.put(7, INTEGER); // COLUMN_SIZE - getTypesDWColumns.put(8, INTEGER); // BUFFER_LENGTH - getTypesDWColumns.put(9, INTEGER); // DECIMAL_DIGITS - getTypesDWColumns.put(10, INTEGER); // NUM_PREC_RADIX - getTypesDWColumns.put(11, INTEGER); // NULLABLE - getTypesDWColumns.put(12, VARCHAR); // REMARKS - getTypesDWColumns.put(13, NVARCHAR); // COLUMN_DEF - getTypesDWColumns.put(14, INTEGER); // SQL_DATA_TYPE - getTypesDWColumns.put(15, INTEGER); // SQL_DATETIME_SUB - getTypesDWColumns.put(16, INTEGER); // CHAR_OCTET_LENGTH - getTypesDWColumns.put(17, INTEGER); // ORDINAL_POSITION - getTypesDWColumns.put(18, VARCHAR); // IS_NULLABLE + getTypesDWColumns.put(1, NVARCHAR); // TABLE_CAT + getTypesDWColumns.put(2, NVARCHAR); // TABLE_SCHEM + getTypesDWColumns.put(3, NVARCHAR); // TABLE_NAME + getTypesDWColumns.put(4, NVARCHAR); // COLUMN_NAME + getTypesDWColumns.put(5, INTEGER); // DATA_TYPE + getTypesDWColumns.put(6, NVARCHAR); // TYPE_NAME + getTypesDWColumns.put(7, INTEGER); // COLUMN_SIZE + getTypesDWColumns.put(8, INTEGER); // BUFFER_LENGTH + getTypesDWColumns.put(9, INTEGER); // DECIMAL_DIGITS + getTypesDWColumns.put(10, INTEGER); // NUM_PREC_RADIX + getTypesDWColumns.put(11, INTEGER); // NULLABLE + getTypesDWColumns.put(12, VARCHAR); // REMARKS + getTypesDWColumns.put(13, NVARCHAR); // COLUMN_DEF + getTypesDWColumns.put(14, INTEGER); // SQL_DATA_TYPE + getTypesDWColumns.put(15, INTEGER); // SQL_DATETIME_SUB + getTypesDWColumns.put(16, INTEGER); // CHAR_OCTET_LENGTH + getTypesDWColumns.put(17, INTEGER); // ORDINAL_POSITION + getTypesDWColumns.put(18, VARCHAR); // IS_NULLABLE /* * Use negative value keys to indicate that this column doesn't exist in SQL Server and should just * be queried as 'NULL' */ - getTypesDWColumns.put(-1, VARCHAR); // SCOPE_CATALOG - getTypesDWColumns.put(-2, VARCHAR); // SCOPE_SCHEMA - getTypesDWColumns.put(-3, VARCHAR); // SCOPE_TABLE - getTypesDWColumns.put(29, SMALLINT); // SOURCE_DATA_TYPE - getTypesDWColumns.put(22, VARCHAR); // IS_AUTOINCREMENT - getTypesDWColumns.put(21, VARCHAR); // IS_GENERATEDCOLUMN - getTypesDWColumns.put(19, SMALLINT); // SS_IS_SPARSE - getTypesDWColumns.put(20, SMALLINT); // SS_IS_COLUMN_SET - getTypesDWColumns.put(23, NVARCHAR); // SS_UDT_CATALOG_NAME - getTypesDWColumns.put(24, NVARCHAR); // SS_UDT_SCHEMA_NAME - getTypesDWColumns.put(25, NVARCHAR); // SS_UDT_ASSEMBLY_TYPE_NAME - getTypesDWColumns.put(26, NVARCHAR); // SS_XML_SCHEMACOLLECTION_CATALOG_NAME - getTypesDWColumns.put(27, NVARCHAR); // SS_XML_SCHEMACOLLECTION_SCHEMA_NAME - getTypesDWColumns.put(28, NVARCHAR); // SS_XML_SCHEMACOLLECTION_NAME + getTypesDWColumns.put(-1, VARCHAR); // SCOPE_CATALOG + getTypesDWColumns.put(-2, VARCHAR); // SCOPE_SCHEMA + getTypesDWColumns.put(-3, VARCHAR); // SCOPE_TABLE + getTypesDWColumns.put(29, SMALLINT); // SOURCE_DATA_TYPE + getTypesDWColumns.put(22, VARCHAR); // IS_AUTOINCREMENT + getTypesDWColumns.put(21, VARCHAR); // IS_GENERATEDCOLUMN + getTypesDWColumns.put(19, SMALLINT); // SS_IS_SPARSE + getTypesDWColumns.put(20, SMALLINT); // SS_IS_COLUMN_SET + getTypesDWColumns.put(23, NVARCHAR); // SS_UDT_CATALOG_NAME + getTypesDWColumns.put(24, NVARCHAR); // SS_UDT_SCHEMA_NAME + getTypesDWColumns.put(25, NVARCHAR); // SS_UDT_ASSEMBLY_TYPE_NAME + getTypesDWColumns.put(26, NVARCHAR); // SS_XML_SCHEMACOLLECTION_CATALOG_NAME + getTypesDWColumns.put(27, NVARCHAR); // SS_XML_SCHEMACOLLECTION_SCHEMA_NAME + getTypesDWColumns.put(28, NVARCHAR); // SS_XML_SCHEMACOLLECTION_NAME } - + // Ensure there is a data type for every metadata column if (getColumnsDWColumns.size() != getTypesDWColumns.size()) { MessageFormat form = new MessageFormat( @@ -837,26 +837,38 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table } } - private String generateAzureDWSelect(ResultSet rs, Map columns, Map types) throws SQLException { + private String generateAzureDWSelect(ResultSet rs, Map columns, + Map types) throws SQLException { StringBuilder sb = new StringBuilder("SELECT "); + for (Entry p : columns.entrySet()) { + String dataType = types.get(p.getKey()); + + // Verify there is a valid column entry in the Data Type lookup map + if (dataType == null) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument")); + Object[] msgArgs = {p.getKey()}; + throw new SQLServerException(null, form.format(msgArgs), null, 0, true); + } + sb.append("CAST("); if (p.getKey() < 0) { - sb.append("NULL AS " + types.get(p.getKey())); + sb.append("NULL AS " + dataType); } else { Object o = rs.getObject(p.getKey()); if (null == o) { - sb.append("NULL AS " + types.get(p.getKey())); + sb.append("NULL AS " + dataType); } else if (o instanceof Number) { if (IS_AUTOINCREMENT.equalsIgnoreCase(p.getValue()) || IS_GENERATEDCOLUMN.equalsIgnoreCase(p.getValue())) { sb.append("'").append(Util.escapeSingleQuotes(Util.zeroOneToYesNo(((Number) o).intValue()))) - .append("' AS ").append(types.get(p.getKey())); + .append("' AS ").append(dataType); } else { - sb.append(o.toString()).append(" AS ").append(types.get(p.getKey())); + sb.append(o.toString()).append(" AS ").append(dataType); } } else { - sb.append("'").append(Util.escapeSingleQuotes(o.toString())).append("' AS ").append(types.get(p.getKey())).append("(").append(Integer.toString(o.toString().length())).append(")"); + sb.append("'").append(Util.escapeSingleQuotes(o.toString())).append("' AS ").append(dataType) + .append("(").append(Integer.toString(o.toString().length())).append(")"); } } sb.append(") AS ").append(p.getValue()).append(","); @@ -864,7 +876,7 @@ private String generateAzureDWSelect(ResultSet rs, Map columns, sb.setLength(sb.length() - 1); return sb.toString(); } - + private String generateAzureDWEmptyRS(Map columns) { StringBuilder sb = new StringBuilder("SELECT TOP 0 "); for (Entry p : columns.entrySet()) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java index 73bf060d8..eaf1be062 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -924,7 +924,7 @@ public void testGetImportedKeysDW() throws SQLException { // Validates the metadata data types defined by JDBC spec // https://docs.oracle.com/javase/8/docs/api/java/sql/DatabaseMetaData.html#getColumns-java.lang.String-java.lang.String-java.lang.String-java.lang.String- public void testValidateColumnMetadata() throws SQLException { - + if (getColumnMetaDataClass == null) { getColumnMetaDataClass = new LinkedHashMap<>(); getColumnMetaDataClass.put(TABLE_CAT, String.class); @@ -934,7 +934,7 @@ public void testValidateColumnMetadata() throws SQLException { getColumnMetaDataClass.put(DATA_TYPE, Integer.class); getColumnMetaDataClass.put(TYPE_NAME, String.class); getColumnMetaDataClass.put(COLUMN_SIZE, Integer.class); - getColumnMetaDataClass.put(BUFFER_LENGTH, Object.class); // Not used + getColumnMetaDataClass.put(BUFFER_LENGTH, Integer.class); // Not used getColumnMetaDataClass.put(DECIMAL_DIGITS, Integer.class); getColumnMetaDataClass.put(NUM_PREC_RADIX, Integer.class); getColumnMetaDataClass.put(NULLABLE, Integer.class); @@ -959,45 +959,28 @@ public void testValidateColumnMetadata() throws SQLException { getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_CATALOG_NAME, String.class); getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, String.class); getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_NAME, String.class); - } - + } + try (Connection conn = getConnection()) { ResultSetMetaData metadata = conn.getMetaData().getColumns(null, null, tableName, null).getMetaData(); + // Ensure that there is an expected class for every column in the metadata result set + assertEquals(metadata.getColumnCount(), getColumnMetaDataClass.size()); + for (int i = 1; i < metadata.getColumnCount(); i++) { - // Ensure that there is a data type for every metadata column String columnLabel = metadata.getColumnLabel(i); String columnClassName = metadata.getColumnClassName(i); - Class columnClass = null; - - try - { - columnClass = Class.forName(columnClassName); - } - catch (ClassNotFoundException ex) - { - MessageFormat form = new MessageFormat( - TestResource.getResource("R_classLoaderNotFoundForColumnType")); - Object[] msgArgs = {columnClassName, columnLabel}; - fail(form.format(msgArgs)); - } - Class expectedClass = getColumnMetaDataClass.get(columnLabel); - - assert(expectedClass != null); - - if (!expectedClass.isAssignableFrom(columnClass)) { - MessageFormat form = new MessageFormat( - TestResource.getResource("R_classNotAssignable")); - Object[] msgArgs = {expectedClass.getName(), columnClassName, columnLabel}; - fail(form.format(msgArgs)); - } + + // Ensure the metadata column is in the metadata column class map + assertNotNull(expectedClass); + + // Ensure the actual and expected column metadata types match + assertEquals(columnClassName, expectedClass.getName()); } - } catch (SQLException e) { - fail(e.getMessage()); } } - + @BeforeAll public static void setupTable() throws Exception { setConnection(); From 71e657df7faceaaf8e7ff4cbf063452fd2b200cd Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Tue, 27 Feb 2024 13:03:03 -0800 Subject: [PATCH 05/10] Change in error handling to determine which column is returning the wrong data type --- .../java/com/microsoft/sqlserver/jdbc/TestResource.java | 3 ++- .../jdbc/databasemetadata/DatabaseMetaDataTest.java | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index 879e83a5b..3ebcabf41 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -214,5 +214,6 @@ protected Object[][] getContents() { "javax.security.auth.login.LoginException (No LoginModules configured for SQLJDBCDriver)"}, {"R_unexpectedThreadCount", "Thread count is higher than expected."}, {"R_classLoaderNotFoundForColumnType", "Class Loader for type {0} not found for column {1}"}, - {"R_classNotAssignable", "Class {0} is not assignable from class {1} for column {2}"}}; + {"R_classNotAssignable", "Class {0} is not assignable from class {1} for column {2}"}, + {"R_expectedClassDoesNotMatchActualClass", "Expected column class {0} does not match actual column class {1} for column {2}."}}; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java index eaf1be062..a6ecebfeb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -976,7 +976,12 @@ public void testValidateColumnMetadata() throws SQLException { assertNotNull(expectedClass); // Ensure the actual and expected column metadata types match - assertEquals(columnClassName, expectedClass.getName()); + if (columnClassName != expectedClass.getName()) { + MessageFormat form1 = new MessageFormat(TestResource.getResource("R_expectedClassDoesNotMatchActualClass")); + Object[] msgArgs1 = {expectedClass.getName(), columnClassName, columnLabel}; + fail(form1.format(msgArgs1)); + + } } } } From cc6c9d7ea51b3b9585e634110486d9d189fe7f01 Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Tue, 27 Feb 2024 16:33:02 -0800 Subject: [PATCH 06/10] Minor code change to try and fix DATA_TYPE column type error --- .../com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 67f120ec0..e7d581a7a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -638,7 +638,7 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table + "INSERT INTO @mssqljdbc_temp_sp_columns_result EXEC sp_columns_100 ?,?,?,?,?,?;" + "SELECT TABLE_QUALIFIER AS TABLE_CAT, TABLE_OWNER AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, " - + "CAST (DATA_TYPE AS INT) AS DATA_TYPE,TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, " + + "CAST(DATA_TYPE AS INT) AS DATA_TYPE,TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, " + "CAST(SCALE AS INT) AS DECIMAL_DIGITS, CAST(RADIX AS INT) AS NUM_PREC_RADIX,CAST(NULLABLE AS INT) AS NULLABLE, " + "CAST(REMARKS AS VARCHAR) AS REMARKS, COLUMN_DEF, CAST(SQL_DATA_TYPE AS INT) AS SQL_DATA_TYPE, " + "CAST(SQL_DATETIME_SUB AS INT) AS SQL_DATETIME_SUB, CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE," From dbc5b283f31a0c1bebfcce3e90144aea6f3d6820 Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Wed, 28 Feb 2024 15:17:20 -0800 Subject: [PATCH 07/10] Commit latest version of driber for teams inspection --- .../microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java | 4 +++- .../sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index e7d581a7a..5074bfebc 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -638,7 +638,7 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table + "INSERT INTO @mssqljdbc_temp_sp_columns_result EXEC sp_columns_100 ?,?,?,?,?,?;" + "SELECT TABLE_QUALIFIER AS TABLE_CAT, TABLE_OWNER AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, " - + "CAST(DATA_TYPE AS INT) AS DATA_TYPE,TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, " + + "CONVERT(INT, DATA_TYPE) AS DATA_TYPE,TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, " + "CAST(SCALE AS INT) AS DECIMAL_DIGITS, CAST(RADIX AS INT) AS NUM_PREC_RADIX,CAST(NULLABLE AS INT) AS NULLABLE, " + "CAST(REMARKS AS VARCHAR) AS REMARKS, COLUMN_DEF, CAST(SQL_DATA_TYPE AS INT) AS SQL_DATA_TYPE, " + "CAST(SQL_DATETIME_SUB AS INT) AS SQL_DATETIME_SUB, CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE," @@ -649,6 +649,7 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table + "SS_IS_SPARSE, SS_IS_COLUMN_SET, SS_UDT_CATALOG_NAME, SS_UDT_SCHEMA_NAME, SS_UDT_ASSEMBLY_TYPE_NAME," + "SS_XML_SCHEMACOLLECTION_CATALOG_NAME, SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, SS_XML_SCHEMACOLLECTION_NAME " + "FROM @mssqljdbc_temp_sp_columns_result ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION;"; +System.out.println("Query: " + spColumnsSql); SQLServerResultSet rs = null; PreparedStatement pstmt = (SQLServerPreparedStatement) this.connection.prepareStatement(spColumnsSql); pstmt.closeOnCompletion(); @@ -810,6 +811,7 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table } else { azureDwSelectBuilder.append(" ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION "); } +System.out.println("Query: " + azureDwSelectBuilder.toString()); resultPstmt = (SQLServerPreparedStatement) this.connection .prepareStatement(azureDwSelectBuilder.toString()); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java index a6ecebfeb..29640c52e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -976,7 +976,7 @@ public void testValidateColumnMetadata() throws SQLException { assertNotNull(expectedClass); // Ensure the actual and expected column metadata types match - if (columnClassName != expectedClass.getName()) { + if (!columnClassName.equals(expectedClass.getName())) { MessageFormat form1 = new MessageFormat(TestResource.getResource("R_expectedClassDoesNotMatchActualClass")); Object[] msgArgs1 = {expectedClass.getName(), columnClassName, columnLabel}; fail(form1.format(msgArgs1)); From 19bacf29f2ca2909dd56fe6e7a21e8f81ff0063c Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Thu, 29 Feb 2024 07:30:03 -0800 Subject: [PATCH 08/10] Modified the CONVERT() back to CAST() in SQL string --- .../com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 5074bfebc..60fcde987 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -638,7 +638,7 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table + "INSERT INTO @mssqljdbc_temp_sp_columns_result EXEC sp_columns_100 ?,?,?,?,?,?;" + "SELECT TABLE_QUALIFIER AS TABLE_CAT, TABLE_OWNER AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, " - + "CONVERT(INT, DATA_TYPE) AS DATA_TYPE,TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, " + + "CAST(DATA_TYPE AS INT) AS DATA_TYPE,TYPE_NAME, PRECISION AS COLUMN_SIZE, LENGTH AS BUFFER_LENGTH, " + "CAST(SCALE AS INT) AS DECIMAL_DIGITS, CAST(RADIX AS INT) AS NUM_PREC_RADIX,CAST(NULLABLE AS INT) AS NULLABLE, " + "CAST(REMARKS AS VARCHAR) AS REMARKS, COLUMN_DEF, CAST(SQL_DATA_TYPE AS INT) AS SQL_DATA_TYPE, " + "CAST(SQL_DATETIME_SUB AS INT) AS SQL_DATETIME_SUB, CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE," From d2d1b05763027ca70673567dcc58df4663ba0926 Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Thu, 29 Feb 2024 08:15:35 -0800 Subject: [PATCH 09/10] Removed debugging output after code review --- .../com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 60fcde987..e7d581a7a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -649,7 +649,6 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table + "SS_IS_SPARSE, SS_IS_COLUMN_SET, SS_UDT_CATALOG_NAME, SS_UDT_SCHEMA_NAME, SS_UDT_ASSEMBLY_TYPE_NAME," + "SS_XML_SCHEMACOLLECTION_CATALOG_NAME, SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, SS_XML_SCHEMACOLLECTION_NAME " + "FROM @mssqljdbc_temp_sp_columns_result ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION;"; -System.out.println("Query: " + spColumnsSql); SQLServerResultSet rs = null; PreparedStatement pstmt = (SQLServerPreparedStatement) this.connection.prepareStatement(spColumnsSql); pstmt.closeOnCompletion(); @@ -811,7 +810,6 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table } else { azureDwSelectBuilder.append(" ORDER BY TABLE_CAT, TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION "); } -System.out.println("Query: " + azureDwSelectBuilder.toString()); resultPstmt = (SQLServerPreparedStatement) this.connection .prepareStatement(azureDwSelectBuilder.toString()); From 0ce310e1e2bb5fc4265fab9ab372af293995a3d2 Mon Sep 17 00:00:00 2001 From: "Barry Ward (Simba Technologies Inc)" Date: Fri, 1 Mar 2024 10:28:32 -0800 Subject: [PATCH 10/10] Changes as requested in code review --- .../jdbc/SQLServerDatabaseMetaData.java | 1 - .../sqlserver/jdbc/TestResource.java | 2 - .../DatabaseMetaDataTest.java | 89 ++++++++++--------- 3 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index e7d581a7a..2dfb40d89 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -25,7 +25,6 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; -import com.microsoft.sqlserver.jdbc.JDBCType; /** diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index 3ebcabf41..607254030 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -213,7 +213,5 @@ protected Object[][] getContents() { {"R_noLoginModulesConfiguredForJdbcDriver", "javax.security.auth.login.LoginException (No LoginModules configured for SQLJDBCDriver)"}, {"R_unexpectedThreadCount", "Thread count is higher than expected."}, - {"R_classLoaderNotFoundForColumnType", "Class Loader for type {0} not found for column {1}"}, - {"R_classNotAssignable", "Class {0} is not assignable from class {1} for column {2}"}, {"R_expectedClassDoesNotMatchActualClass", "Expected column class {0} does not match actual column class {1} for column {2}."}}; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java index 29640c52e..999ccdc1e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -63,7 +63,6 @@ public class DatabaseMetaDataTest extends AbstractTest { private static final String functionName = RandomUtil.getIdentifier("DBMetadataFunction"); private static Map getColumnsDWColumns = null; private static Map getImportedKeysDWColumns = null; - private static Map> getColumnMetaDataClass = null; private static final String TABLE_CAT = "TABLE_CAT"; private static final String TABLE_SCHEM = "TABLE_SCHEM"; private static final String TABLE_NAME = "TABLE_NAME"; @@ -920,46 +919,48 @@ public void testGetImportedKeysDW() throws SQLException { } } + /** + * Validates the metadata data types defined by JDBC spec. + * Refer to DatabaseMetadata getColumns() specs + * + * @throws SQLException + */ @Test - // Validates the metadata data types defined by JDBC spec - // https://docs.oracle.com/javase/8/docs/api/java/sql/DatabaseMetaData.html#getColumns-java.lang.String-java.lang.String-java.lang.String-java.lang.String- public void testValidateColumnMetadata() throws SQLException { - - if (getColumnMetaDataClass == null) { - getColumnMetaDataClass = new LinkedHashMap<>(); - getColumnMetaDataClass.put(TABLE_CAT, String.class); - getColumnMetaDataClass.put(TABLE_SCHEM, String.class); - getColumnMetaDataClass.put(TABLE_NAME, String.class); - getColumnMetaDataClass.put(COLUMN_NAME, String.class); - getColumnMetaDataClass.put(DATA_TYPE, Integer.class); - getColumnMetaDataClass.put(TYPE_NAME, String.class); - getColumnMetaDataClass.put(COLUMN_SIZE, Integer.class); - getColumnMetaDataClass.put(BUFFER_LENGTH, Integer.class); // Not used - getColumnMetaDataClass.put(DECIMAL_DIGITS, Integer.class); - getColumnMetaDataClass.put(NUM_PREC_RADIX, Integer.class); - getColumnMetaDataClass.put(NULLABLE, Integer.class); - getColumnMetaDataClass.put(REMARKS, String.class); - getColumnMetaDataClass.put(COLUMN_DEF, String.class); - getColumnMetaDataClass.put(SQL_DATA_TYPE, Integer.class); - getColumnMetaDataClass.put(SQL_DATETIME_SUB, Integer.class); - getColumnMetaDataClass.put(CHAR_OCTET_LENGTH, Integer.class); - getColumnMetaDataClass.put(ORDINAL_POSITION, Integer.class); - getColumnMetaDataClass.put(IS_NULLABLE, String.class); - getColumnMetaDataClass.put(SCOPE_CATALOG, String.class); - getColumnMetaDataClass.put(SCOPE_SCHEMA, String.class); - getColumnMetaDataClass.put(SCOPE_TABLE, String.class); - getColumnMetaDataClass.put(SOURCE_DATA_TYPE, Short.class); - getColumnMetaDataClass.put(IS_AUTOINCREMENT, String.class); - getColumnMetaDataClass.put(IS_GENERATEDCOLUMN, String.class); - getColumnMetaDataClass.put(SS_IS_SPARSE, Short.class); - getColumnMetaDataClass.put(SS_IS_COLUMN_SET, Short.class); - getColumnMetaDataClass.put(SS_UDT_CATALOG_NAME, String.class); - getColumnMetaDataClass.put(SS_UDT_SCHEMA_NAME, String.class); - getColumnMetaDataClass.put(SS_UDT_ASSEMBLY_TYPE_NAME, String.class); - getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_CATALOG_NAME, String.class); - getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, String.class); - getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_NAME, String.class); - } + Map> getColumnMetaDataClass = new LinkedHashMap<>(); + + getColumnMetaDataClass.put(TABLE_CAT, String.class); + getColumnMetaDataClass.put(TABLE_SCHEM, String.class); + getColumnMetaDataClass.put(TABLE_NAME, String.class); + getColumnMetaDataClass.put(COLUMN_NAME, String.class); + getColumnMetaDataClass.put(DATA_TYPE, Integer.class); + getColumnMetaDataClass.put(TYPE_NAME, String.class); + getColumnMetaDataClass.put(COLUMN_SIZE, Integer.class); + getColumnMetaDataClass.put(BUFFER_LENGTH, Integer.class); // Not used + getColumnMetaDataClass.put(DECIMAL_DIGITS, Integer.class); + getColumnMetaDataClass.put(NUM_PREC_RADIX, Integer.class); + getColumnMetaDataClass.put(NULLABLE, Integer.class); + getColumnMetaDataClass.put(REMARKS, String.class); + getColumnMetaDataClass.put(COLUMN_DEF, String.class); + getColumnMetaDataClass.put(SQL_DATA_TYPE, Integer.class); + getColumnMetaDataClass.put(SQL_DATETIME_SUB, Integer.class); + getColumnMetaDataClass.put(CHAR_OCTET_LENGTH, Integer.class); + getColumnMetaDataClass.put(ORDINAL_POSITION, Integer.class); + getColumnMetaDataClass.put(IS_NULLABLE, String.class); + getColumnMetaDataClass.put(SCOPE_CATALOG, String.class); + getColumnMetaDataClass.put(SCOPE_SCHEMA, String.class); + getColumnMetaDataClass.put(SCOPE_TABLE, String.class); + getColumnMetaDataClass.put(SOURCE_DATA_TYPE, Short.class); + getColumnMetaDataClass.put(IS_AUTOINCREMENT, String.class); + getColumnMetaDataClass.put(IS_GENERATEDCOLUMN, String.class); + getColumnMetaDataClass.put(SS_IS_SPARSE, Short.class); + getColumnMetaDataClass.put(SS_IS_COLUMN_SET, Short.class); + getColumnMetaDataClass.put(SS_UDT_CATALOG_NAME, String.class); + getColumnMetaDataClass.put(SS_UDT_SCHEMA_NAME, String.class); + getColumnMetaDataClass.put(SS_UDT_ASSEMBLY_TYPE_NAME, String.class); + getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_CATALOG_NAME, String.class); + getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, String.class); + getColumnMetaDataClass.put(SS_XML_SCHEMACOLLECTION_NAME, String.class); try (Connection conn = getConnection()) { ResultSetMetaData metadata = conn.getMetaData().getColumns(null, null, tableName, null).getMetaData(); @@ -973,14 +974,18 @@ public void testValidateColumnMetadata() throws SQLException { Class expectedClass = getColumnMetaDataClass.get(columnLabel); // Ensure the metadata column is in the metadata column class map - assertNotNull(expectedClass); + if (expectedClass == null) { + MessageFormat form1 = new MessageFormat(TestResource.getResource("R_objectNullOrEmpty")); + Object[] msgArgs1 = {"expected metadata column class for column " + columnLabel}; + fail(form1.format(msgArgs1)); + } // Ensure the actual and expected column metadata types match if (!columnClassName.equals(expectedClass.getName())) { - MessageFormat form1 = new MessageFormat(TestResource.getResource("R_expectedClassDoesNotMatchActualClass")); + MessageFormat form1 = new MessageFormat( + TestResource.getResource("R_expectedClassDoesNotMatchActualClass")); Object[] msgArgs1 = {expectedClass.getName(), columnClassName, columnLabel}; fail(form1.format(msgArgs1)); - } } }