Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix to ensure metadata returned follows JDBC data type specs #2326

Merged
merged 10 commits into from
Mar 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Jeffery-Wasty marked this conversation as resolved.
Show resolved Hide resolved

private static final String SQL_KEYWORDS = createSqlKeyWords();

// Use LinkedHashMap to force retrieve elements in order they were inserted
/** getColumns columns */
private LinkedHashMap<Integer, String> getColumnsDWColumns = null;
private LinkedHashMap<Integer, String> getTypesDWColumns = null;
/** getImportedKeys columns */
private volatile LinkedHashMap<Integer, String> getImportedKeysDWColumns;
private static final Lock LOCK = new ReentrantLock();
Expand Down Expand Up @@ -630,10 +636,13 @@ 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,"
+ "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,"
Expand Down Expand Up @@ -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());
Jeffery-Wasty marked this conversation as resolved.
Show resolved Hide resolved
} finally {
LOCK.unlock();
}
Expand All @@ -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;
}

Expand Down Expand Up @@ -780,33 +831,34 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table
}
}

private String generateAzureDWSelect(ResultSet rs, Map<Integer, String> columns) throws SQLException {
private String generateAzureDWSelect(ResultSet rs, Map<Integer, String> columns, Map<Integer, String> types) throws SQLException {
StringBuilder sb = new StringBuilder("SELECT ");
for (Entry<Integer, String> 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<Integer, String> columns) {
StringBuilder sb = new StringBuilder("SELECT TOP 0 ");
for (Entry<Integer, String> p : columns.entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,39 @@ public class DatabaseMetaDataTest extends AbstractTest {
private static final String functionName = RandomUtil.getIdentifier("DBMetadataFunction");
private static Map<Integer, String> getColumnsDWColumns = null;
private static Map<Integer, String> getImportedKeysDWColumns = null;
private static Map<Integer, Integer> 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.
Expand Down Expand Up @@ -887,6 +920,57 @@ public void testGetImportedKeysDW() throws SQLException {
}
}

@Test
public void testValidateColumnMetadata() throws SQLException {
lilgreenbird marked this conversation as resolved.
Show resolved Hide resolved
Jeffery-Wasty marked this conversation as resolved.
Show resolved Hide resolved

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();
Expand Down
Loading