diff --git a/src/main/java/com/firebolt/jdbc/resultset/FireboltResultSetMetaData.java b/src/main/java/com/firebolt/jdbc/resultset/FireboltResultSetMetaData.java index ae7449ea17..0806a008bb 100644 --- a/src/main/java/com/firebolt/jdbc/resultset/FireboltResultSetMetaData.java +++ b/src/main/java/com/firebolt/jdbc/resultset/FireboltResultSetMetaData.java @@ -79,6 +79,10 @@ public String getColumnTypeName(int column) throws SQLException { return getColumn(column).getType().getCompactTypeName(); } + public String getFullyQualifiedColumnTypeName(int column) throws SQLException { + return getColumn(column).getType().getName(); + } + @Override public String getColumnClassName(int column) throws SQLException { return getColumn(column).getType().getDataType().getBaseType().getType().getName(); diff --git a/src/test/java/com/firebolt/jdbc/resultset/FireboltResultSetTest.java b/src/test/java/com/firebolt/jdbc/resultset/FireboltResultSetTest.java index 3c47080d79..b759c74da2 100644 --- a/src/test/java/com/firebolt/jdbc/resultset/FireboltResultSetTest.java +++ b/src/test/java/com/firebolt/jdbc/resultset/FireboltResultSetTest.java @@ -35,6 +35,7 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Calendar; +import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import java.util.concurrent.Callable; @@ -1387,6 +1388,148 @@ void shouldReturnDateTimeObjectsWithProvidedTypes() throws SQLException { assertArrayEquals(new Integer[] { 1, 2, 3, 4 }, ((Integer[]) resultSet.getObject(9, Array.class).getArray())); } + @Test + void shouldReturnExpectedFullyQualifiedColumnType() throws SQLException { + inputStream = getInputStreamWithAllTypes(); + resultSet = createResultSet(inputStream); + resultSet.next(); + FireboltResultSetMetaData metaData = resultSet.getMetaData().unwrap(FireboltResultSetMetaData.class); + + // Expected column metadata: column index -> (expectedType, expectedTypeName) + // Type names are in uppercase as returned by getFullyQualifiedColumnTypeName + Map expectedColumns = new HashMap<>(); + // int columns + expectedColumns.put(1, new ColumnTypeAssertion(Types.INTEGER, "INT")); + expectedColumns.put(2, new ColumnTypeAssertion(Types.INTEGER, "INT NULL")); + expectedColumns.put(3, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(INT)")); + expectedColumns.put(4, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(INT NULL)")); + expectedColumns.put(5, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(INT) NULL")); + expectedColumns.put(6, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(INT))")); + expectedColumns.put(7, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(INT NULL) NULL)")); + expectedColumns.put(8, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(INT)) NULL")); + // long columns + expectedColumns.put(9, new ColumnTypeAssertion(Types.BIGINT, "LONG")); + expectedColumns.put(10, new ColumnTypeAssertion(Types.BIGINT, "LONG NULL")); + expectedColumns.put(11, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(LONG)")); + expectedColumns.put(12, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(LONG NULL)")); + expectedColumns.put(13, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(LONG) NULL")); + expectedColumns.put(14, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(LONG))")); + expectedColumns.put(15, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(LONG NULL) NULL)")); + expectedColumns.put(16, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(LONG)) NULL")); + // float columns + expectedColumns.put(17, new ColumnTypeAssertion(Types.REAL, "FLOAT")); + expectedColumns.put(18, new ColumnTypeAssertion(Types.REAL, "FLOAT NULL")); + expectedColumns.put(19, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(FLOAT)")); + expectedColumns.put(20, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(FLOAT NULL)")); + expectedColumns.put(21, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(FLOAT) NULL")); + expectedColumns.put(22, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(FLOAT))")); + expectedColumns.put(23, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(FLOAT NULL) NULL)")); + expectedColumns.put(24, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(FLOAT)) NULL")); + // double columns + expectedColumns.put(25, new ColumnTypeAssertion(Types.DOUBLE, "DOUBLE")); + expectedColumns.put(26, new ColumnTypeAssertion(Types.DOUBLE, "DOUBLE NULL")); + expectedColumns.put(27, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(DOUBLE)")); + expectedColumns.put(28, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(DOUBLE NULL)")); + expectedColumns.put(29, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(DOUBLE) NULL")); + expectedColumns.put(30, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DOUBLE))")); + expectedColumns.put(31, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DOUBLE NULL) NULL)")); + expectedColumns.put(32, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DOUBLE)) NULL")); + // text columns + expectedColumns.put(33, new ColumnTypeAssertion(Types.VARCHAR, "TEXT")); + expectedColumns.put(34, new ColumnTypeAssertion(Types.VARCHAR, "TEXT NULL")); + expectedColumns.put(35, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(TEXT)")); + expectedColumns.put(36, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(TEXT NULL)")); + expectedColumns.put(37, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(TEXT) NULL")); + expectedColumns.put(38, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TEXT))")); + expectedColumns.put(39, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TEXT NULL) NULL)")); + expectedColumns.put(40, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TEXT)) NULL")); + // date columns + expectedColumns.put(41, new ColumnTypeAssertion(Types.DATE, "DATE")); + expectedColumns.put(42, new ColumnTypeAssertion(Types.DATE, "DATE NULL")); + expectedColumns.put(43, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(DATE)")); + expectedColumns.put(44, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(DATE NULL)")); + expectedColumns.put(45, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(DATE) NULL")); + expectedColumns.put(46, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DATE) NULL)")); + expectedColumns.put(47, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DATE))")); + expectedColumns.put(48, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DATE NULL) NULL)")); + expectedColumns.put(49, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DATE)) NULL")); + // timestamp columns + expectedColumns.put(50, new ColumnTypeAssertion(Types.TIMESTAMP, "TIMESTAMP")); + expectedColumns.put(51, new ColumnTypeAssertion(Types.TIMESTAMP, "TIMESTAMP NULL")); + expectedColumns.put(52, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(TIMESTAMP)")); + expectedColumns.put(53, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(TIMESTAMP NULL)")); + expectedColumns.put(54, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(TIMESTAMP) NULL")); + expectedColumns.put(55, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TIMESTAMP) NULL)")); + expectedColumns.put(56, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TIMESTAMP))")); + expectedColumns.put(57, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TIMESTAMP NULL) NULL)")); + expectedColumns.put(58, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TIMESTAMP)) NULL")); + // timestamptz columns + expectedColumns.put(59, new ColumnTypeAssertion(Types.TIMESTAMP_WITH_TIMEZONE, "TIMESTAMPTZ")); + expectedColumns.put(60, new ColumnTypeAssertion(Types.TIMESTAMP_WITH_TIMEZONE, "TIMESTAMPTZ NULL")); + expectedColumns.put(61, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(TIMESTAMPTZ)")); + expectedColumns.put(62, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(TIMESTAMPTZ NULL)")); + expectedColumns.put(63, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(TIMESTAMPTZ) NULL")); + expectedColumns.put(64, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TIMESTAMPTZ) NULL)")); + expectedColumns.put(65, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TIMESTAMPTZ))")); + expectedColumns.put(66, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TIMESTAMPTZ NULL) NULL)")); + expectedColumns.put(67, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(TIMESTAMPTZ)) NULL")); + // boolean columns + expectedColumns.put(68, new ColumnTypeAssertion(Types.BOOLEAN, "BOOLEAN")); + expectedColumns.put(69, new ColumnTypeAssertion(Types.BOOLEAN, "BOOLEAN NULL")); + expectedColumns.put(70, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(BOOLEAN)")); + expectedColumns.put(71, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(BOOLEAN NULL)")); + expectedColumns.put(72, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(BOOLEAN) NULL")); + expectedColumns.put(73, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(BOOLEAN))")); + expectedColumns.put(74, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(BOOLEAN NULL) NULL)")); + expectedColumns.put(75, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(BOOLEAN)) NULL")); + // decimal columns + expectedColumns.put(76, new ColumnTypeAssertion(Types.NUMERIC, "DECIMAL(38, 30)")); + expectedColumns.put(77, new ColumnTypeAssertion(Types.NUMERIC, "DECIMAL(38, 30) NULL")); + expectedColumns.put(78, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(DECIMAL(38, 30))")); + expectedColumns.put(79, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(DECIMAL(38, 30) NULL)")); + expectedColumns.put(80, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(DECIMAL(38, 30)) NULL")); + expectedColumns.put(81, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DECIMAL(38, 30)) NULL)")); + expectedColumns.put(82, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DECIMAL(38, 30)))")); + expectedColumns.put(83, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DECIMAL(38, 30) NULL) NULL)")); + expectedColumns.put(84, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(DECIMAL(38, 30))) NULL")); + // bytea columns + expectedColumns.put(85, new ColumnTypeAssertion(Types.BINARY, "BYTEA")); + expectedColumns.put(86, new ColumnTypeAssertion(Types.BINARY, "BYTEA NULL")); + // geography columns + expectedColumns.put(87, new ColumnTypeAssertion(Types.VARCHAR, "GEOGRAPHY")); + expectedColumns.put(88, new ColumnTypeAssertion(Types.VARCHAR, "GEOGRAPHY NULL")); + expectedColumns.put(89, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(GEOGRAPHY)")); + expectedColumns.put(90, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(GEOGRAPHY NULL)")); + expectedColumns.put(91, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(GEOGRAPHY) NULL")); + expectedColumns.put(92, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(GEOGRAPHY))")); + expectedColumns.put(93, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(GEOGRAPHY NULL) NULL)")); + expectedColumns.put(94, new ColumnTypeAssertion(Types.ARRAY, "ARRAY(ARRAY(GEOGRAPHY)) NULL")); + + // Verify all expected columns + assertEquals(expectedColumns.size(), metaData.getColumnCount(), "Should have expected number of columns"); + + for (Map.Entry entry : expectedColumns.entrySet()) { + int columnIndex = entry.getKey(); + ColumnTypeAssertion expected = entry.getValue(); + + assertEquals(expected.columnType, metaData.getColumnType(columnIndex), + format("Column type should match for column %d", columnIndex)); + assertEquals(expected.typeName, metaData.getFullyQualifiedColumnTypeName(columnIndex), + format("Column type name should match for column %d. Expected: %s, Actual: %s", + columnIndex, expected.typeName, metaData.getFullyQualifiedColumnTypeName(columnIndex))); + } + } + + private static class ColumnTypeAssertion { + final int columnType; + final String typeName; + + ColumnTypeAssertion(int columnType, String typeName) { + this.columnType = columnType; + this.typeName = typeName; + } + } + @Test void shouldThrowExceptionWhenConvertingIncompatibleTypes() throws SQLException { inputStream = getInputStreamWithCommonResponseExample(); @@ -1627,6 +1770,10 @@ private InputStream getInputStreamWithNewTypes() { return FireboltResultSetTest.class.getResourceAsStream("/responses/firebolt-response-with-new-types"); } + private InputStream getInputStreamWithAllTypes() { + return FireboltResultSetTest.class.getResourceAsStream("/responses/firebolt-response-with-all-types"); + } + private InputStream getInputStreamWithNumericTypes() { return FireboltResultSetTest.class.getResourceAsStream("/responses/firebolt-response-with-numeric-types.csv"); } diff --git a/src/test/resources/responses/firebolt-response-with-all-types b/src/test/resources/responses/firebolt-response-with-all-types new file mode 100644 index 0000000000..2ca001e86a --- /dev/null +++ b/src/test/resources/responses/firebolt-response-with-all-types @@ -0,0 +1,3 @@ +col_int col_int_null col_int_array col_int_null_array col_int_array_null col_int_array_null_array col_int_null_array_null_array col_int_array_array_null col_long col_long_null col_long_array col_long_null_array col_long_array_null col_long_array_null_array col_long_null_array_null_array col_long_array_array_null col_float col_float_null col_float_array col_float_null_array col_float_array_null col_float_array_null_array col_float_null_array_null_array col_float_array_array_null col_double col_double_null col_double_array col_double_null_array col_double_array_null col_double_array_null_array col_double_null_array_null_array col_double_array_array_null col_text col_text_null col_text_array col_text_null_array col_text_array_null col_text_array_null_array col_text_null_array_null_array col_text_array_array_null col_date col_date_null col_date_array col_date_null_array col_date_array_null col_date_array_null_array col_date_array_array col_date_null_array_null_array col_date_array_array_null col_timestamp col_timestamp_null col_timestamp_array col_timestamp_null_array col_timestamp_array_null col_timestamp_array_null_array col_timestamp_array_array col_timestamp_null_array_null_array col_timestamp_array_array_null col_timestamptz col_timestamptz_null col_timestamptz_array col_timestamptz_null_array col_timestamptz_array_null col_timestamptz_array_null_array col_timestamptz_array_array col_timestamptz_null_array_null_array col_timestamptz_array_array_null col_boolean col_boolean_null col_boolean_array col_boolean_null_array col_boolean_array_null col_boolean_array_null_array col_boolean_null_array_null_array col_boolean_array_array_null col_decimal col_decimal_null col_decimal_array col_decimal_null_array col_decimal_array_null col_decimal_array_null_array col_decimal_array_array col_decimal_null_array_null_array col_decimal_array_array_null col_bytea col_bytea_null col_geography col_geography_null col_geography_array col_geography_null_array col_geography_array_null col_geography_array_null_array col_geography_null_array_null_array col_geography_array_array_null +int int null array(int) array(int null) array(int) null array(array(int)) array(array(int null) null) array(array(int)) null long long null array(long) array(long null) array(long) null array(array(long)) array(array(long null) null) array(array(long)) null float float null array(float) array(float null) array(float) null array(array(float)) array(array(float null) null) array(array(float)) null double double null array(double) array(double null) array(double) null array(array(double)) array(array(double null) null) array(array(double)) null text text null array(text) array(text null) array(text) null array(array(text)) array(array(text null) null) array(array(text)) null date date null array(date) array(date null) array(date) null array(array(date) null) array(array(date)) array(array(date null) null) array(array(date)) null timestamp timestamp null array(timestamp) array(timestamp null) array(timestamp) null array(array(timestamp) null) array(array(timestamp)) array(array(timestamp null) null) array(array(timestamp)) null timestamptz timestamptz null array(timestamptz) array(timestamptz null) array(timestamptz) null array(array(timestamptz) null) array(array(timestamptz)) array(array(timestamptz null) null) array(array(timestamptz)) null boolean boolean null array(boolean) array(boolean null) array(boolean) null array(array(boolean)) array(array(boolean null) null) array(array(boolean)) null Decimal(38, 30) Decimal(38, 30) null array(Decimal(38, 30)) array(Decimal(38, 30) null) array(Decimal(38, 30)) null array(array(Decimal(38, 30)) null) array(array(Decimal(38, 30))) array(array(Decimal(38, 30) null) null) array(array(Decimal(38, 30))) null bytea bytea null geography geography null array(geography) array(geography null) array(geography) null array(array(geography)) array(array(geography null) null) array(array(geography)) null +1 \N {1,2} {1,NULL} \N {{1,2},{}} {{1,NULL},NULL} \N 30000000000 \N {1,2,3} {1,NULL} \N {{1,2},{}} {{1,NULL},NULL} \N 1.23 \N {1.25,2.5} {1.25,NULL} \N {{1.25,2.5},{}} {{1.25,NULL},NULL} \N 1.23456789012 \N {1.125,2.25} {1.125,NULL} \N {{1.125,2.25},{}} {{1.125,NULL},NULL} \N text \N {a,b} {a,NULL} \N {{a,b},{}} {{a,NULL},NULL} \N 2021-03-28 \N {2021-03-28,2021-03-29} {2021-03-28,NULL} \N {{2021-03-28,2021-03-29},NULL} {{2021-03-28,2021-03-29},{}} {{2021-03-28,NULL},NULL} \N 2019-07-31 01:01:01 \N {"2019-07-31 01:01:01","2019-08-01 00:00:00"} {"2019-07-31 01:01:01",NULL} \N {{"2019-07-31 01:01:01","2019-08-01 00:00:00"},NULL} {{"2019-07-31 01:01:01","2019-08-01 00:00:00"},{}} {{"2019-07-31 01:01:01",NULL},NULL} \N 1111-01-05 17:04:42.123456+00 \N {"1111-01-05 17:04:42.123456+00","1111-01-06 17:04:42.123456+00"} {"1111-01-05 17:04:42.123456+00",NULL} \N {{"1111-01-05 17:04:42.123456+00","1111-01-06 17:04:42.123456+00"},NULL} {{"1111-01-05 17:04:42.123456+00","1111-01-06 17:04:42.123456+00"},{}} {{"1111-01-05 17:04:42.123456+00",NULL},NULL} \N t \N {t,f} {t,NULL} \N {{t,f},{}} {{t,NULL},NULL} \N 1231232.123459999990457054844258706536 \N {1.500000000000000000000000000000,-2.250000000000000000000000000000} {1.500000000000000000000000000000,NULL} \N {{1.500000000000000000000000000000,-2.250000000000000000000000000000},NULL} {{1.500000000000000000000000000000,-2.250000000000000000000000000000},{}} {{1.500000000000000000000000000000,NULL},NULL} \N \x616263313233 \N 0101000020E6100000FEFFFFFFFFFFEF3F0000000000000040 \N {0101000020E6100000FEFFFFFFFFFFEF3F0000000000000040,0101000020E6100000FEFFFFFFFFFFFF3F0100000000000840} {0101000020E6100000FEFFFFFFFFFFEF3F0000000000000040,NULL} \N {{0101000020E6100000FEFFFFFFFFFFEF3F0000000000000040,0101000020E6100000FEFFFFFFFFFFFF3F0100000000000840},{}} {{0101000020E6100000FEFFFFFFFFFFEF3F0000000000000040,NULL},NULL} \N