diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java index ebf18e4c389f..1c6834feb27d 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java @@ -42,6 +42,7 @@ import com.google.cloud.bigquery.TableDefinition; import com.google.cloud.bigquery.TableId; import com.google.cloud.bigquery.exception.BigQueryJdbcException; +import com.google.common.collect.ImmutableMap; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -59,6 +60,7 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Scanner; import java.util.Set; @@ -1650,8 +1652,8 @@ ColumnTypeInfo determineTypeInfoFromDataType( String columnName, int ordinalPosition) { - ColumnTypeInfo defaultVarcharTypeInfo = - new ColumnTypeInfo(Types.VARCHAR, "VARCHAR", null, null, null); + ColumnTypeInfo defaultStringTypeInfo = + new ColumnTypeInfo(Types.NVARCHAR, "STRING", null, null, null); try { String typeKind = argumentDataType.getTypeKind(); if (typeKind != null && !typeKind.isEmpty()) { @@ -1664,10 +1666,10 @@ ColumnTypeInfo determineTypeInfoFromDataType( } catch (Exception e) { LOG.warning( "Proc: %s, Arg: %s (Pos %d) - Caught an unexpected Exception during type" - + " determination. Defaulting type to VARCHAR. Error: %s", + + " determination. Defaulting type to STRING. Error: %s", procedureName, columnName, ordinalPosition, e.getMessage()); } - return defaultVarcharTypeInfo; + return defaultStringTypeInfo; } Comparator defineGetProcedureColumnsComparator(FieldList resultSchemaFields) { @@ -4750,13 +4752,16 @@ public boolean generatedKeyAlwaysReturned() { } @Override - public T unwrap(Class iface) { - return null; + public T unwrap(Class iface) throws SQLException { + if (iface.isInstance(this)) { + return iface.cast(this); + } + throw new SQLException("Cannot unwrap to " + iface.getName()); } @Override - public boolean isWrapperFor(Class iface) { - return false; + public boolean isWrapperFor(Class iface) throws SQLException { + return iface != null && iface.isInstance(this); } // --- Helper Methods --- @@ -4828,47 +4833,58 @@ private Tuple determineEffectiveCatalogAndSchema( return Tuple.of(effectiveCatalog, effectiveSchemaPattern); } + // BigQuery STRING represents Unicode character strings (UTF-8). + // Standard JDBC maps UTF-8/Unicode data to Types.NVARCHAR rather than Types.VARCHAR. + private static final Map STANDARD_TYPE_INFO = + ImmutableMap.builder() + .put(StandardSQLTypeName.INT64, new ColumnTypeInfo(Types.BIGINT, "INT64", 19, 0, 10)) + .put(StandardSQLTypeName.BOOL, new ColumnTypeInfo(Types.BOOLEAN, "BOOL", 1, null, null)) + .put( + StandardSQLTypeName.FLOAT64, + new ColumnTypeInfo(Types.DOUBLE, "FLOAT64", 15, null, 10)) + .put(StandardSQLTypeName.NUMERIC, new ColumnTypeInfo(Types.NUMERIC, "NUMERIC", 38, 9, 10)) + .put( + StandardSQLTypeName.BIGNUMERIC, + new ColumnTypeInfo(Types.NUMERIC, "BIGNUMERIC", 77, 38, 10)) + .put( + StandardSQLTypeName.STRING, + new ColumnTypeInfo(Types.NVARCHAR, "STRING", null, null, null)) + .put( + StandardSQLTypeName.TIMESTAMP, + new ColumnTypeInfo(Types.TIMESTAMP, "TIMESTAMP", 29, null, null)) + .put( + StandardSQLTypeName.DATETIME, + new ColumnTypeInfo(Types.TIMESTAMP, "DATETIME", 29, null, null)) + .put(StandardSQLTypeName.DATE, new ColumnTypeInfo(Types.DATE, "DATE", 10, null, null)) + .put(StandardSQLTypeName.TIME, new ColumnTypeInfo(Types.TIME, "TIME", 15, null, null)) + .put( + StandardSQLTypeName.GEOGRAPHY, + new ColumnTypeInfo(Types.OTHER, "GEOGRAPHY", null, null, null)) + .put(StandardSQLTypeName.JSON, new ColumnTypeInfo(Types.OTHER, "JSON", null, null, null)) + .put( + StandardSQLTypeName.INTERVAL, + new ColumnTypeInfo(Types.OTHER, "INTERVAL", null, null, null)) + .put( + StandardSQLTypeName.RANGE, new ColumnTypeInfo(Types.OTHER, "RANGE", null, null, null)) + .put( + StandardSQLTypeName.BYTES, + new ColumnTypeInfo(Types.VARBINARY, "BYTES", null, null, null)) + .put( + StandardSQLTypeName.STRUCT, + new ColumnTypeInfo(Types.STRUCT, "STRUCT", null, null, null)) + .build(); + private ColumnTypeInfo getColumnTypeInfoForSqlType(StandardSQLTypeName bqType) { if (bqType == null) { - LOG.warning("Null BigQuery type encountered: " + bqType.name() + ". Mapping to VARCHAR."); - return new ColumnTypeInfo(Types.VARCHAR, bqType.name(), null, null, null); + LOG.warning("Null BigQuery type encountered. Mapping to STRING."); + return new ColumnTypeInfo(Types.NVARCHAR, "STRING", null, null, null); } - - switch (bqType) { - case INT64: - return new ColumnTypeInfo(Types.BIGINT, "BIGINT", 19, 0, 10); - case BOOL: - return new ColumnTypeInfo(Types.BOOLEAN, "BOOLEAN", 1, null, null); - case FLOAT64: - return new ColumnTypeInfo(Types.DOUBLE, "DOUBLE", 15, null, 10); - case NUMERIC: - return new ColumnTypeInfo(Types.NUMERIC, "NUMERIC", 38, 9, 10); - case BIGNUMERIC: - return new ColumnTypeInfo(Types.NUMERIC, "NUMERIC", 77, 38, 10); - case STRING: - return new ColumnTypeInfo(Types.NVARCHAR, "NVARCHAR", null, null, null); - case TIMESTAMP: - case DATETIME: - return new ColumnTypeInfo(Types.TIMESTAMP, "TIMESTAMP", 29, null, null); - case DATE: - return new ColumnTypeInfo(Types.DATE, "DATE", 10, null, null); - case TIME: - return new ColumnTypeInfo(Types.TIME, "TIME", 15, null, null); - case GEOGRAPHY: - return new ColumnTypeInfo(Types.OTHER, "GEOGRAPHY", null, null, null); - case JSON: - return new ColumnTypeInfo(Types.OTHER, "JSON", null, null, null); - case INTERVAL: - return new ColumnTypeInfo(Types.OTHER, "INTERVAL", null, null, null); - case BYTES: - return new ColumnTypeInfo(Types.VARBINARY, "VARBINARY", null, null, null); - case STRUCT: - return new ColumnTypeInfo(Types.STRUCT, "STRUCT", null, null, null); - default: - LOG.warning( - "Unknown BigQuery type encountered: " + bqType.name() + ". Mapping to VARCHAR."); - return new ColumnTypeInfo(Types.VARCHAR, bqType.name(), null, null, null); + ColumnTypeInfo info = STANDARD_TYPE_INFO.get(bqType); + if (info == null) { + LOG.warning("Unknown BigQuery type encountered: " + bqType.name() + ". Mapping to STRING."); + return new ColumnTypeInfo(Types.NVARCHAR, "STRING", null, null, null); } + return info; } List findMatchingBigQueryObjects( diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcContextProxy.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcContextProxy.java index 990a9ed10659..360fba0a8ed1 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcContextProxy.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcContextProxy.java @@ -109,7 +109,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl // Support standard JDBC Wrapper unwrap operations if (methodName.equals("unwrap") && args != null && args.length == 1) { Class iface = (Class) args[0]; - if (iface.isInstance(target)) { + if (iface != null && iface.isInstance(target)) { return target; } try { @@ -120,7 +120,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } if (methodName.equals("isWrapperFor") && args != null && args.length == 1) { Class iface = (Class) args[0]; - if (iface.isInstance(target)) { + if (iface != null && iface.isInstance(target)) { return true; } try { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java index 976e91b46aa4..5d577d8392f8 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java @@ -20,7 +20,6 @@ import com.google.cloud.bigquery.Field.Mode; import com.google.cloud.bigquery.FieldList; import com.google.cloud.bigquery.StandardSQLTypeName; -import com.google.cloud.bigquery.exception.BigQueryJdbcSqlFeatureNotSupportedException; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; @@ -74,8 +73,7 @@ public boolean isCaseSensitive(int column) { @Override public boolean isSearchable(int column) { - int colType = getColumnType(column); - return colType != Types.OTHER; + return true; } @Override @@ -205,14 +203,17 @@ public String getColumnClassName(int column) { .getName(); } - // Unsupported methods: + // Wrapper methods: @Override public T unwrap(Class iface) throws SQLException { - throw new BigQueryJdbcSqlFeatureNotSupportedException("unwrap is not implemented"); + if (iface.isInstance(this)) { + return iface.cast(this); + } + throw new SQLException("Cannot unwrap to " + iface.getName()); } @Override public boolean isWrapperFor(Class iface) throws SQLException { - throw new BigQueryJdbcSqlFeatureNotSupportedException("isWrapperFor is not implemented"); + return iface != null && iface.isInstance(this); } } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java index 9915a744ee70..744e8fd194a7 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java @@ -251,7 +251,7 @@ public void testMapBigQueryTypeToJdbc_ScalarTypes() { BigQueryDatabaseMetaData.ColumnTypeInfo infoInt64 = dbMetadata.mapBigQueryTypeToJdbc(fieldInt64); assertEquals(Types.BIGINT, infoInt64.jdbcType); - assertEquals("BIGINT", infoInt64.typeName); + assertEquals("INT64", infoInt64.typeName); assertEquals(Integer.valueOf(19), infoInt64.columnSize); assertEquals(Integer.valueOf(0), infoInt64.decimalDigits); assertEquals(Integer.valueOf(10), infoInt64.numPrecRadix); @@ -264,7 +264,7 @@ public void testMapBigQueryTypeToJdbc_ScalarTypes() { BigQueryDatabaseMetaData.ColumnTypeInfo infoString = dbMetadata.mapBigQueryTypeToJdbc(fieldString); assertEquals(Types.NVARCHAR, infoString.jdbcType); - assertEquals("NVARCHAR", infoString.typeName); + assertEquals("STRING", infoString.typeName); assertNull(infoString.columnSize); assertNull(infoString.decimalDigits); assertNull(infoString.numPrecRadix); @@ -276,7 +276,7 @@ public void testMapBigQueryTypeToJdbc_ScalarTypes() { .build(); BigQueryDatabaseMetaData.ColumnTypeInfo infoBool = dbMetadata.mapBigQueryTypeToJdbc(fieldBool); assertEquals(Types.BOOLEAN, infoBool.jdbcType); - assertEquals("BOOLEAN", infoBool.typeName); + assertEquals("BOOL", infoBool.typeName); assertEquals(Integer.valueOf(1), infoBool.columnSize); // BYTES -> VARBINARY @@ -287,7 +287,7 @@ public void testMapBigQueryTypeToJdbc_ScalarTypes() { BigQueryDatabaseMetaData.ColumnTypeInfo infoBytes = dbMetadata.mapBigQueryTypeToJdbc(fieldBytes); assertEquals(Types.VARBINARY, infoBytes.jdbcType); - assertEquals("VARBINARY", infoBytes.typeName); + assertEquals("BYTES", infoBytes.typeName); assertNull(infoBytes.columnSize); // TIMESTAMP -> TIMESTAMP @@ -311,7 +311,7 @@ public void testMapBigQueryTypeToJdbc_ScalarTypes() { BigQueryDatabaseMetaData.ColumnTypeInfo infoDateTime = dbMetadata.mapBigQueryTypeToJdbc(fieldDateTime); assertEquals(Types.TIMESTAMP, infoDateTime.jdbcType); - assertEquals("TIMESTAMP", infoDateTime.typeName); + assertEquals("DATETIME", infoDateTime.typeName); assertEquals(Integer.valueOf(29), infoDateTime.columnSize); assertNull(infoDateTime.decimalDigits); assertNull(infoDateTime.numPrecRadix); @@ -337,7 +337,7 @@ public void testMapBigQueryTypeToJdbc_ScalarTypes() { BigQueryDatabaseMetaData.ColumnTypeInfo infoBigNumeric = dbMetadata.mapBigQueryTypeToJdbc(fieldBigNumeric); assertEquals(Types.NUMERIC, infoBigNumeric.jdbcType); - assertEquals("NUMERIC", infoBigNumeric.typeName); + assertEquals("BIGNUMERIC", infoBigNumeric.typeName); assertEquals(Integer.valueOf(77), infoBigNumeric.columnSize); assertEquals(Integer.valueOf(38), infoBigNumeric.decimalDigits); assertEquals(Integer.valueOf(10), infoBigNumeric.numPrecRadix); @@ -445,7 +445,7 @@ public void testCreateColumnRow() { assertEquals(table, row.get(2).getStringValue()); // 3. TABLE_NAME assertEquals("user_name", row.get(3).getStringValue()); // 4. COLUMN_NAME assertEquals(String.valueOf(Types.NVARCHAR), row.get(4).getStringValue()); // 5. DATA_TYPE - assertEquals("NVARCHAR", row.get(5).getStringValue()); // 6. TYPE_NAME + assertEquals("STRING", row.get(5).getStringValue()); // 6. TYPE_NAME assertTrue(row.get(6).isNull()); // 7. COLUMN_SIZE (was null for STRING) assertTrue(row.get(7).isNull()); // 8. BUFFER_LENGTH (always null) assertTrue(row.get(8).isNull()); // 9. DECIMAL_DIGITS (null for STRING) @@ -480,7 +480,7 @@ public void testCreateColumnRow_RequiredInt() { assertEquals(24, row.size()); assertEquals("user_id", row.get(3).getStringValue()); // COLUMN_NAME assertEquals(String.valueOf(Types.BIGINT), row.get(4).getStringValue()); // DATA_TYPE - assertEquals("BIGINT", row.get(5).getStringValue()); // TYPE_NAME + assertEquals("INT64", row.get(5).getStringValue()); // TYPE_NAME assertEquals("19", row.get(6).getStringValue()); // COLUMN_SIZE assertEquals("0", row.get(8).getStringValue()); // DECIMAL_DIGITS assertEquals("10", row.get(9).getStringValue()); // NUM_PREC_RADIX @@ -1160,21 +1160,21 @@ public void testDetermineTypeInfoFromDataType() { BigQueryDatabaseMetaData.ColumnTypeInfo infoInt64 = dbMetadata.determineTypeInfoFromDataType(sqlInt64, "p", "c", 1); assertEquals(Types.BIGINT, infoInt64.jdbcType); - assertEquals("BIGINT", infoInt64.typeName); + assertEquals("INT64", infoInt64.typeName); // STRING StandardSQLDataType sqlString = mockStandardSQLDataType(StandardSQLTypeName.STRING); BigQueryDatabaseMetaData.ColumnTypeInfo infoString = dbMetadata.determineTypeInfoFromDataType(sqlString, "p", "c", 1); assertEquals(Types.NVARCHAR, infoString.jdbcType); - assertEquals("NVARCHAR", infoString.typeName); + assertEquals("STRING", infoString.typeName); // BOOL StandardSQLDataType sqlBool = mockStandardSQLDataType(StandardSQLTypeName.BOOL); BigQueryDatabaseMetaData.ColumnTypeInfo infoBool = dbMetadata.determineTypeInfoFromDataType(sqlBool, "p", "c", 1); assertEquals(Types.BOOLEAN, infoBool.jdbcType); - assertEquals("BOOLEAN", infoBool.typeName); + assertEquals("BOOL", infoBool.typeName); // STRUCT StandardSQLDataType sqlStruct = mockStandardSQLDataType(StandardSQLTypeName.STRUCT); @@ -1183,21 +1183,21 @@ public void testDetermineTypeInfoFromDataType() { assertEquals(Types.STRUCT, infoStruct.jdbcType); assertEquals("STRUCT", infoStruct.typeName); - // Case: null typeKind from StandardSQLDataType (should default to VARCHAR) + // Case: null typeKind from StandardSQLDataType (should default to STRING) StandardSQLDataType sqlNullKind = mock(StandardSQLDataType.class); when(sqlNullKind.getTypeKind()).thenReturn(null); BigQueryDatabaseMetaData.ColumnTypeInfo infoNullKind = dbMetadata.determineTypeInfoFromDataType(sqlNullKind, "p", "c", 1); - assertEquals(Types.VARCHAR, infoNullKind.jdbcType); - assertEquals("VARCHAR", infoNullKind.typeName); + assertEquals(Types.NVARCHAR, infoNullKind.jdbcType); + assertEquals("STRING", infoNullKind.typeName); - // Case: unknown typeKind from StandardSQLDataType (should default to VARCHAR) + // Case: unknown typeKind from StandardSQLDataType (should default to STRING) StandardSQLDataType sqlUnknownKind = mock(StandardSQLDataType.class); when(sqlUnknownKind.getTypeKind()).thenReturn("SUPER_DOOPER_TYPE"); BigQueryDatabaseMetaData.ColumnTypeInfo infoUnknownKind = dbMetadata.determineTypeInfoFromDataType(sqlUnknownKind, "p", "c", 1); - assertEquals(Types.VARCHAR, infoUnknownKind.jdbcType); - assertEquals("VARCHAR", infoUnknownKind.typeName); + assertEquals(Types.NVARCHAR, infoUnknownKind.jdbcType); + assertEquals("STRING", infoUnknownKind.typeName); } @Test @@ -1225,7 +1225,7 @@ public void testCreateProcedureColumnRow_BasicInParam() { String.valueOf(DatabaseMetaData.procedureColumnIn), row.get(4).getStringValue()); // 5. COLUMN_TYPE assertEquals(String.valueOf(Types.NVARCHAR), row.get(5).getStringValue()); // 6. DATA_TYPE - assertEquals("NVARCHAR", row.get(6).getStringValue()); // 7. TYPE_NAME + assertEquals("STRING", row.get(6).getStringValue()); // 7. TYPE_NAME assertTrue(row.get(7).isNull()); // 8. PRECISION assertTrue(row.get(8).isNull()); // 9. LENGTH assertTrue(row.get(9).isNull()); // 10. SCALE @@ -2706,7 +2706,7 @@ public void testCreateFunctionColumnRow() { assertEquals("param_in", row.get(3).getStringValue()); assertEquals(String.valueOf(DatabaseMetaData.functionColumnIn), row.get(4).getStringValue()); assertEquals(String.valueOf(Types.NVARCHAR), row.get(5).getStringValue()); // DATA_TYPE - assertEquals("NVARCHAR", row.get(6).getStringValue()); // TYPE_NAME + assertEquals("STRING", row.get(6).getStringValue()); // TYPE_NAME assertTrue(row.get(7).isNull()); // PRECISION assertTrue(row.get(8).isNull()); // LENGTH assertTrue(row.get(9).isNull()); // SCALE @@ -3228,6 +3228,24 @@ public void testGetSQLStateType() throws SQLException { assertEquals(DatabaseMetaData.sqlStateSQL, dbMetadata.getSQLStateType()); } + @Test + public void testWrapperMethods() throws SQLException { + assertTrue(dbMetadata.isWrapperFor(DatabaseMetaData.class)); + assertTrue(dbMetadata.isWrapperFor(BigQueryDatabaseMetaData.class)); + assertFalse(dbMetadata.isWrapperFor(java.sql.Connection.class)); + assertFalse(dbMetadata.isWrapperFor(null)); + + assertSame(dbMetadata, dbMetadata.unwrap(DatabaseMetaData.class)); + assertSame(dbMetadata, dbMetadata.unwrap(BigQueryDatabaseMetaData.class)); + + try { + dbMetadata.unwrap(java.sql.Connection.class); + org.junit.jupiter.api.Assertions.fail("Should have thrown SQLException"); + } catch (SQLException e) { + assertTrue(e.getMessage().contains("Cannot unwrap to java.sql.Connection")); + } + } + @Test public void testMetadataMethodsDoNotInterfere() throws SQLException { Statement mockStatement1 = mock(Statement.class); diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadataTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadataTest.java index 6e7c9147bae8..3b4e5971adfc 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadataTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadataTest.java @@ -274,4 +274,45 @@ public void testNestedScale() throws SQLException { public void testNestedColumnDisplaySize() throws SQLException { assertThat(resultSetMetaDataNested.getColumnDisplaySize(1)).isEqualTo(50); } + + @Test + public void testWrapperMethods() throws SQLException { + assertThat(resultSetMetaData.isWrapperFor(ResultSetMetaData.class)).isTrue(); + assertThat(resultSetMetaData.isWrapperFor(BigQueryResultSetMetadata.class)).isTrue(); + assertThat(resultSetMetaData.isWrapperFor(java.sql.Connection.class)).isFalse(); + assertThat(resultSetMetaData.isWrapperFor(null)).isFalse(); + + Object unwrappedMeta = resultSetMetaData.unwrap(ResultSetMetaData.class); + assertThat(unwrappedMeta).isNotSameInstanceAs(resultSetMetaData); + assertThat(unwrappedMeta).isInstanceOf(BigQueryResultSetMetadata.class); + + Object unwrappedImpl = resultSetMetaData.unwrap(BigQueryResultSetMetadata.class); + assertThat(unwrappedImpl).isNotSameInstanceAs(resultSetMetaData); + assertThat(unwrappedImpl).isInstanceOf(BigQueryResultSetMetadata.class); + + try { + resultSetMetaData.unwrap(java.sql.Connection.class); + org.junit.jupiter.api.Assertions.fail("Should have thrown SQLException"); + } catch (SQLException e) { + assertThat(e.getMessage()).contains("Cannot unwrap to java.sql.Connection"); + } + } + + @Test + public void testIsSearchableForOtherTypes() throws SQLException { + FieldList schemaFields = + FieldList.of( + Field.of("geo", StandardSQLTypeName.GEOGRAPHY), + Field.of("json", StandardSQLTypeName.JSON), + Field.of("interval", StandardSQLTypeName.INTERVAL), + Field.of("range", StandardSQLTypeName.RANGE)); + BigQueryJsonResultSet otherTypesResultSet = + BigQueryJsonResultSet.of( + Schema.of(schemaFields), 1L, null, statement, new Thread[] {new Thread()}); + ResultSetMetaData otherMetaData = otherTypesResultSet.getMetaData(); + assertThat(otherMetaData.isSearchable(1)).isTrue(); // GEOGRAPHY + assertThat(otherMetaData.isSearchable(2)).isTrue(); // JSON + assertThat(otherMetaData.isSearchable(3)).isTrue(); // INTERVAL + assertThat(otherMetaData.isSearchable(4)).isTrue(); // RANGE + } }