From 690c8608ae969574101baad31aa368dfcbbb29c4 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Fri, 22 May 2026 16:15:53 -0400 Subject: [PATCH 1/3] feat(bigquery-jdbc): implement JDBC getObject type coercion support --- .../bigquery/jdbc/BigQueryBaseResultSet.java | 19 +++++++ .../bigquery/jdbc/BigQueryNoOpsResultSet.java | 10 ---- .../jdbc/BigQueryTypeCoercionUtility.java | 41 ++++++++++++++ .../jdbc/BigQueryArrowResultSetTest.java | 52 ++++++++++++++++++ .../jdbc/BigQueryJsonResultSetTest.java | 54 +++++++++++++++++++ 5 files changed, 166 insertions(+), 10 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java index fdfcaefe2361..10b914d65560 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java @@ -246,6 +246,25 @@ public boolean isClosed() { public abstract Object getObject(int columnIndex) throws SQLException; + @Override + public T getObject(int columnIndex, Class type) throws SQLException { + LOG.finestTrace("getObject"); + try { + Object value = getObject(columnIndex); + if (value == null) { + return null; + } + return this.bigQueryTypeCoercer.coerceTo(type, value, this.LOG); + } catch (BigQueryJdbcCoercionNotFoundException e) { + throw createCoercionException(columnIndex, type, e); + } + } + + @Override + public T getObject(String columnLabel, Class type) throws SQLException { + return getObject(getColumnIndex(columnLabel), type); + } + protected int getColumnIndex(String columnLabel) throws SQLException { LOG.finestTrace("getColumnIndex"); checkClosed(); diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryNoOpsResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryNoOpsResultSet.java index e4b29f7cd566..e6437ae940fb 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryNoOpsResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryNoOpsResultSet.java @@ -655,16 +655,6 @@ public void updateNClob(String columnLabel, Reader reader) throws SQLException { throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED); } - @Override - public T getObject(int columnIndex, Class type) throws SQLException { - throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED); - } - - @Override - public T getObject(String columnLabel, Class type) throws SQLException { - throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED); - } - @Override public T unwrap(Class iface) throws SQLException { throw new BigQueryJdbcSqlFeatureNotSupportedException(METHOD_NOT_IMPLEMENTED); diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java index ae51736473dd..b47965ca52d7 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java @@ -29,9 +29,12 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.OffsetDateTime; import java.time.Period; +import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; +import java.util.TimeZone; import java.util.concurrent.TimeUnit; import org.apache.arrow.vector.PeriodDuration; import org.apache.arrow.vector.util.Text; @@ -86,6 +89,37 @@ class BigQueryTypeCoercionUtility { Timestamp.class) .registerTypeCoercion( (Date date) -> new Timestamp(date.getTime()), Date.class, Timestamp.class) + .registerTypeCoercion( + (LocalDateTime ldt) -> Date.valueOf(ldt.toLocalDate()), + LocalDateTime.class, + Date.class) + .registerTypeCoercion( + (LocalDateTime ldt) -> { + long millisOfDay = + java.util.concurrent.TimeUnit.NANOSECONDS.toMillis( + ldt.toLocalTime().toNanoOfDay()); + long localMillis = TimeZoneCache.getLocalMillis(millisOfDay); + return new Time(localMillis); + }, + LocalDateTime.class, + Time.class) + .registerTypeCoercion((Date date) -> date.toLocalDate(), Date.class, LocalDate.class) + .registerTypeCoercion( + (Time time) -> { + long millis = time.getTime(); + long localMillis = millis + TimeZone.getDefault().getOffset(millis); + return LocalTime.ofNanoOfDay( + java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(localMillis)); + }, + Time.class, + LocalTime.class) + .registerTypeCoercion( + (Timestamp ts) -> ts.toLocalDateTime(), Timestamp.class, LocalDateTime.class) + .registerTypeCoercion( + (Timestamp ts) -> ts.toInstant().atOffset(ZoneOffset.UTC), + Timestamp.class, + OffsetDateTime.class) + .registerTypeCoercion((Timestamp ts) -> ts.toInstant(), Timestamp.class, Instant.class) .registerTypeCoercion(new TimestampToString()) .registerTypeCoercion(new TimeToString()) .registerTypeCoercion((Long l) -> l != 0L, Long.class, Boolean.class) @@ -106,6 +140,13 @@ class BigQueryTypeCoercionUtility { (Boolean b) -> b ? BigDecimal.ONE : BigDecimal.ZERO, Boolean.class, BigDecimal.class) + .registerTypeCoercion( + (Integer i) -> BigDecimal.valueOf(i), Integer.class, BigDecimal.class) + .registerTypeCoercion((Long l) -> BigDecimal.valueOf(l), Long.class, BigDecimal.class) + .registerTypeCoercion( + (Double d) -> BigDecimal.valueOf(d), Double.class, BigDecimal.class) + .registerTypeCoercion((Float f) -> BigDecimal.valueOf(f), Float.class, BigDecimal.class) + .registerTypeCoercion((String s) -> new BigDecimal(s), String.class, BigDecimal.class) .registerTypeCoercion(new PeriodDurationToString()) .registerTypeCoercion(unused -> (byte) 0, Void.class, Byte.class) .registerTypeCoercion(unused -> 0, Void.class, Integer.class) diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java index 442ebff3cd3c..b8371f553386 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.apache.arrow.vector.types.Types.MinorType.INT; import static org.apache.arrow.vector.types.Types.MinorType.VARCHAR; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import com.google.cloud.bigquery.Field; @@ -32,10 +33,14 @@ import com.google.cloud.bigquery.storage.v1.ArrowSchema; import com.google.common.collect.ImmutableList; import java.io.IOException; +import java.math.BigDecimal; import java.sql.Array; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Struct; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.Arrays; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -346,6 +351,53 @@ public void testIterationNested() throws SQLException { assertThat(bigQueryArrowResultSetNested.isAfterLast()).isTrue(); } + @Test + public void testGetObjectWithType() throws SQLException { + assertThat(bigQueryArrowResultSet.next()).isTrue(); + + // java.time types + assertThat(bigQueryArrowResultSet.getObject("dateField", LocalDate.class)) + .isEqualTo(LocalDate.of(1970, 1, 1)); + assertThat(bigQueryArrowResultSet.getObject(11, LocalDate.class)) + .isEqualTo(LocalDate.of(1970, 1, 1)); + + assertThat(bigQueryArrowResultSet.getObject("timeField", LocalTime.class)) + .isEqualTo(LocalTime.of(0, 0, 1, 234_000_000)); + assertThat(bigQueryArrowResultSet.getObject(10, LocalTime.class)) + .isEqualTo(LocalTime.of(0, 0, 1, 234_000_000)); + + assertThat(bigQueryArrowResultSet.getObject("timeStampField", LocalDateTime.class)) + .isEqualTo(LocalDateTime.of(1970, 1, 1, 0, 0, 0, 10_000_000)); + assertThat(bigQueryArrowResultSet.getObject(5, LocalDateTime.class)) + .isEqualTo(LocalDateTime.of(1970, 1, 1, 0, 0, 0, 10_000_000)); + + // Boolean + assertThat(bigQueryArrowResultSet.getObject("boolField", Boolean.class)).isFalse(); + assertThat(bigQueryArrowResultSet.getObject(1, Boolean.class)).isFalse(); + + // Numbers & Coercions + assertThat(bigQueryArrowResultSet.getObject("int64Filed", Long.class)).isEqualTo(1L); + assertThat(bigQueryArrowResultSet.getObject(2, Integer.class)).isEqualTo(1); + assertThat(bigQueryArrowResultSet.getObject(2, String.class)).isEqualTo("1"); + + assertThat(bigQueryArrowResultSet.getObject("float64Field", Double.class)) + .isEqualTo(Double.valueOf(1.1f)); + + // String + assertThat(bigQueryArrowResultSet.getObject("stringField", String.class)).isEqualTo("text1"); + + // BigDecimal / Numeric + assertThat(bigQueryArrowResultSet.getObject("numericField", BigDecimal.class)) + .isEqualTo(BigDecimal.ONE); + assertThat(bigQueryArrowResultSet.getObject(9, Long.class)).isEqualTo(1L); + + // Unsupported coercions should fail + assertThrows( + SQLException.class, () -> bigQueryArrowResultSet.getObject("boolField", LocalDate.class)); + assertThrows( + SQLException.class, () -> bigQueryArrowResultSet.getObject("dateField", Boolean.class)); + } + private int resultSetRowCount(BigQueryArrowResultSet resultSet) throws SQLException { int rowCount = 0; while (resultSet.next()) { diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java index 6f0ade90c3a4..4198470173db 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java @@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat; import static java.time.Month.MARCH; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import com.google.cloud.bigquery.Field; @@ -47,6 +48,7 @@ import java.sql.Struct; import java.sql.Time; import java.sql.Timestamp; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Calendar; @@ -466,6 +468,58 @@ public void testDate() throws SQLException { .isEqualTo(bigQueryJsonResultSet.getDate("fourteenth", calendar).getTime()); } + @Test + public void testGetObjectWithType() throws SQLException { + assertThat(resetResultSet()).isTrue(); + + // java.time types + assertThat(bigQueryJsonResultSet.getObject("fourteenth", LocalDate.class)) + .isEqualTo(LocalDate.of(2020, 1, 15)); + assertThat(bigQueryJsonResultSet.getObject(14, LocalDate.class)) + .isEqualTo(LocalDate.of(2020, 1, 15)); + + assertThat(bigQueryJsonResultSet.getObject("twelfth", LocalTime.class)) + .isEqualTo(LocalTime.of(11, 14, 19, 820000000)); + assertThat(bigQueryJsonResultSet.getObject(12, LocalTime.class)) + .isEqualTo(LocalTime.of(11, 14, 19, 820000000)); + + assertThat(bigQueryJsonResultSet.getObject("fifth", LocalDateTime.class)) + .isEqualTo(LocalDateTime.of(2023, 3, 30, 11, 14, 19, 820000000)); + assertThat(bigQueryJsonResultSet.getObject(5, LocalDateTime.class)) + .isEqualTo(LocalDateTime.of(2023, 3, 30, 11, 14, 19, 820000000)); + + // Boolean + assertThat(bigQueryJsonResultSet.getObject("first", Boolean.class)).isFalse(); + assertThat(bigQueryJsonResultSet.getObject(1, Boolean.class)).isFalse(); + + // Numbers & Coercions + assertThat(bigQueryJsonResultSet.getObject("second", Long.class)).isEqualTo(1L); + assertThat(bigQueryJsonResultSet.getObject(2, Integer.class)).isEqualTo(1); + assertThat(bigQueryJsonResultSet.getObject(2, Short.class)).isEqualTo((short) 1); + assertThat(bigQueryJsonResultSet.getObject(2, String.class)).isEqualTo("1"); + + assertThat(bigQueryJsonResultSet.getObject("third", Double.class)).isEqualTo(1.5D); + assertThat(bigQueryJsonResultSet.getObject(3, Float.class)).isEqualTo(1.5F); + + // String + assertThat(bigQueryJsonResultSet.getObject("fourth", String.class)).isEqualTo(STRING_VAL); + + // BigDecimal / Numeric + assertThat(bigQueryJsonResultSet.getObject("tenth", BigDecimal.class)) + .isEqualTo(new BigDecimal("12345678")); + assertThat(bigQueryJsonResultSet.getObject(10, Long.class)).isEqualTo(12345678L); + + assertThat(bigQueryJsonResultSet.getObject("eleventh", BigDecimal.class)) + .isEqualTo(new BigDecimal("12345678.99")); + assertThat(bigQueryJsonResultSet.getObject(11, Double.class)).isEqualTo(12345678.99D); + + // Unsupported coercions should fail + assertThrows( + SQLException.class, () -> bigQueryJsonResultSet.getObject("first", LocalDate.class)); + assertThrows( + SQLException.class, () -> bigQueryJsonResultSet.getObject("fourteenth", Boolean.class)); + } + private int resultSetRowCount(BigQueryJsonResultSet resultSet) throws SQLException { int rowCount = 0; while (resultSet.next()) { From 15948cfd7cb410dbfc25249921ee08469d6ab169 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Fri, 22 May 2026 16:50:11 -0400 Subject: [PATCH 2/3] address comments --- .../bigquery/jdbc/BigQueryBaseResultSet.java | 2 +- .../jdbc/BigQueryTypeCoercionUtility.java | 30 ++++++++++++------- .../jdbc/BigQueryArrowResultSetTest.java | 10 ++++--- .../bigquery/jdbc/BigQueryConnectionTest.java | 6 +++- .../jdbc/BigQueryJsonResultSetTest.java | 4 ++- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java index 10b914d65560..a455560f1af7 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java @@ -255,7 +255,7 @@ public T getObject(int columnIndex, Class type) throws SQLException { return null; } return this.bigQueryTypeCoercer.coerceTo(type, value, this.LOG); - } catch (BigQueryJdbcCoercionNotFoundException e) { + } catch (RuntimeException e) { throw createCoercionException(columnIndex, type, e); } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java index b47965ca52d7..13f035cee830 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java @@ -68,23 +68,30 @@ class BigQueryTypeCoercionUtility { .registerTypeCoercion(new BytesArrayToString()) // Read API Type coercions - .registerTypeCoercion(Timestamp::valueOf, LocalDateTime.class, Timestamp.class) + .registerTypeCoercion( + (LocalDateTime ldt) -> Timestamp.from(ldt.toInstant(ZoneOffset.UTC)), + LocalDateTime.class, + Timestamp.class) .registerTypeCoercion(Text::toString, Text.class, String.class) .registerTypeCoercion(new TextToInteger()) .registerTypeCoercion(new LongToTimestamp()) .registerTypeCoercion(new LongToTime()) .registerTypeCoercion(new IntegerToDate()) .registerTypeCoercion( - (Timestamp ts) -> Date.valueOf(ts.toLocalDateTime().toLocalDate()), + (Timestamp ts) -> + Date.valueOf(ts.toInstant().atOffset(ZoneOffset.UTC).toLocalDate()), Timestamp.class, Date.class) .registerTypeCoercion( - (Timestamp ts) -> Time.valueOf(ts.toLocalDateTime().toLocalTime()), + (Timestamp ts) -> + Time.valueOf(ts.toInstant().atOffset(ZoneOffset.UTC).toLocalTime()), Timestamp.class, Time.class) .registerTypeCoercion( (Time time) -> // Per JDBC spec, the date component should be 1970-01-01 - Timestamp.valueOf(LocalDateTime.of(LocalDate.ofEpochDay(0), time.toLocalTime())), + Timestamp.from( + LocalDateTime.of(LocalDate.ofEpochDay(0), time.toLocalTime()) + .toInstant(ZoneOffset.UTC)), Time.class, Timestamp.class) .registerTypeCoercion( @@ -95,9 +102,9 @@ class BigQueryTypeCoercionUtility { Date.class) .registerTypeCoercion( (LocalDateTime ldt) -> { - long millisOfDay = - java.util.concurrent.TimeUnit.NANOSECONDS.toMillis( - ldt.toLocalTime().toNanoOfDay()); + // Custom conversion is used to preserve sub-second (millisecond) precision, + // as standard java.sql.Time.valueOf(LocalTime) truncates milliseconds. + long millisOfDay = TimeUnit.NANOSECONDS.toMillis(ldt.toLocalTime().toNanoOfDay()); long localMillis = TimeZoneCache.getLocalMillis(millisOfDay); return new Time(localMillis); }, @@ -106,15 +113,18 @@ class BigQueryTypeCoercionUtility { .registerTypeCoercion((Date date) -> date.toLocalDate(), Date.class, LocalDate.class) .registerTypeCoercion( (Time time) -> { + // Custom conversion is used to preserve sub-second (millisecond) precision, + // as standard java.sql.Time.toLocalTime() truncates milliseconds. long millis = time.getTime(); long localMillis = millis + TimeZone.getDefault().getOffset(millis); - return LocalTime.ofNanoOfDay( - java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(localMillis)); + return LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos(localMillis)); }, Time.class, LocalTime.class) .registerTypeCoercion( - (Timestamp ts) -> ts.toLocalDateTime(), Timestamp.class, LocalDateTime.class) + (Timestamp ts) -> ts.toInstant().atOffset(ZoneOffset.UTC).toLocalDateTime(), + Timestamp.class, + LocalDateTime.class) .registerTypeCoercion( (Timestamp ts) -> ts.toInstant().atOffset(ZoneOffset.UTC), Timestamp.class, diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java index b8371f553386..d7efce22426f 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java @@ -115,7 +115,7 @@ private VectorSchemaRoot getTestVectorSchemaRoot() { Float8Vector float64Field = new Float8Vector("float64Field", allocator); // Mapped with StandardSQLTypeName.FLOAT64 float64Field.allocateNew(2); - float64Field.set(0, 1.1f); + float64Field.set(0, 1.1); float64Field.setValueCount(1); VarCharVector stringField = new VarCharVector("stringField", allocator); // Mapped with StandardSQLTypeName.STRING @@ -380,8 +380,7 @@ public void testGetObjectWithType() throws SQLException { assertThat(bigQueryArrowResultSet.getObject(2, Integer.class)).isEqualTo(1); assertThat(bigQueryArrowResultSet.getObject(2, String.class)).isEqualTo("1"); - assertThat(bigQueryArrowResultSet.getObject("float64Field", Double.class)) - .isEqualTo(Double.valueOf(1.1f)); + assertThat(bigQueryArrowResultSet.getObject("float64Field", Double.class)).isEqualTo(1.1); // String assertThat(bigQueryArrowResultSet.getObject("stringField", String.class)).isEqualTo("text1"); @@ -391,11 +390,14 @@ public void testGetObjectWithType() throws SQLException { .isEqualTo(BigDecimal.ONE); assertThat(bigQueryArrowResultSet.getObject(9, Long.class)).isEqualTo(1L); - // Unsupported coercions should fail + // Unsupported/malformed coercions should fail assertThrows( SQLException.class, () -> bigQueryArrowResultSet.getObject("boolField", LocalDate.class)); assertThrows( SQLException.class, () -> bigQueryArrowResultSet.getObject("dateField", Boolean.class)); + assertThrows( + SQLException.class, + () -> bigQueryArrowResultSet.getObject("stringField", BigDecimal.class)); } private int resultSetRowCount(BigQueryArrowResultSet resultSet) throws SQLException { diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java index 252d252588bc..8cf8b330ac49 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java @@ -16,7 +16,11 @@ package com.google.cloud.bigquery.jdbc; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.api.gax.rpc.HeaderProvider; diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java index 4198470173db..c22fdc7d0048 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java @@ -513,11 +513,13 @@ public void testGetObjectWithType() throws SQLException { .isEqualTo(new BigDecimal("12345678.99")); assertThat(bigQueryJsonResultSet.getObject(11, Double.class)).isEqualTo(12345678.99D); - // Unsupported coercions should fail + // Unsupported/malformed coercions should fail assertThrows( SQLException.class, () -> bigQueryJsonResultSet.getObject("first", LocalDate.class)); assertThrows( SQLException.class, () -> bigQueryJsonResultSet.getObject("fourteenth", Boolean.class)); + assertThrows( + SQLException.class, () -> bigQueryJsonResultSet.getObject("fourth", BigDecimal.class)); } private int resultSetRowCount(BigQueryJsonResultSet resultSet) throws SQLException { From 8529c9ba8d05fdf223890c5539be42afb8674a0c Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Fri, 22 May 2026 16:54:49 -0400 Subject: [PATCH 3/3] add parameterized tests --- .../jdbc/BigQueryArrowResultSetTest.java | 99 +++++++++-------- .../jdbc/BigQueryJsonResultSetTest.java | 104 +++++++++--------- 2 files changed, 109 insertions(+), 94 deletions(-) diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java index d7efce22426f..1bae7d10b6d5 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSetTest.java @@ -45,6 +45,7 @@ import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; +import java.util.stream.Stream; import org.apache.arrow.memory.RootAllocator; import org.apache.arrow.vector.BitVector; import org.apache.arrow.vector.DateMilliVector; @@ -64,6 +65,9 @@ import org.apache.arrow.vector.util.Text; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; public class BigQueryArrowResultSetTest { @@ -351,53 +355,58 @@ public void testIterationNested() throws SQLException { assertThat(bigQueryArrowResultSetNested.isAfterLast()).isTrue(); } - @Test - public void testGetObjectWithType() throws SQLException { + public static Stream successfulCoercionCases() { + return Stream.of( + Arguments.of("dateField", LocalDate.class, LocalDate.of(1970, 1, 1)), + Arguments.of(11, LocalDate.class, LocalDate.of(1970, 1, 1)), + Arguments.of("timeField", LocalTime.class, LocalTime.of(0, 0, 1, 234_000_000)), + Arguments.of(10, LocalTime.class, LocalTime.of(0, 0, 1, 234_000_000)), + Arguments.of( + "timeStampField", + LocalDateTime.class, + LocalDateTime.of(1970, 1, 1, 0, 0, 0, 10_000_000)), + Arguments.of(5, LocalDateTime.class, LocalDateTime.of(1970, 1, 1, 0, 0, 0, 10_000_000)), + Arguments.of("boolField", Boolean.class, false), + Arguments.of(1, Boolean.class, false), + Arguments.of("int64Filed", Long.class, 1L), + Arguments.of(2, Integer.class, 1), + Arguments.of(2, String.class, "1"), + Arguments.of("float64Field", Double.class, 1.1), + Arguments.of("stringField", String.class, "text1"), + Arguments.of("numericField", BigDecimal.class, BigDecimal.ONE), + Arguments.of(9, Long.class, 1L)); + } + + public static Stream failingCoercionCases() { + return Stream.of( + Arguments.of("boolField", LocalDate.class), + Arguments.of("dateField", Boolean.class), + Arguments.of("stringField", BigDecimal.class)); + } + + @ParameterizedTest + @MethodSource("successfulCoercionCases") + public void testGetObjectWithType_success(Object column, Class type, Object expectedValue) + throws SQLException { assertThat(bigQueryArrowResultSet.next()).isTrue(); + if (column instanceof String) { + assertThat(bigQueryArrowResultSet.getObject((String) column, type)).isEqualTo(expectedValue); + } else { + assertThat(bigQueryArrowResultSet.getObject((Integer) column, type)).isEqualTo(expectedValue); + } + } - // java.time types - assertThat(bigQueryArrowResultSet.getObject("dateField", LocalDate.class)) - .isEqualTo(LocalDate.of(1970, 1, 1)); - assertThat(bigQueryArrowResultSet.getObject(11, LocalDate.class)) - .isEqualTo(LocalDate.of(1970, 1, 1)); - - assertThat(bigQueryArrowResultSet.getObject("timeField", LocalTime.class)) - .isEqualTo(LocalTime.of(0, 0, 1, 234_000_000)); - assertThat(bigQueryArrowResultSet.getObject(10, LocalTime.class)) - .isEqualTo(LocalTime.of(0, 0, 1, 234_000_000)); - - assertThat(bigQueryArrowResultSet.getObject("timeStampField", LocalDateTime.class)) - .isEqualTo(LocalDateTime.of(1970, 1, 1, 0, 0, 0, 10_000_000)); - assertThat(bigQueryArrowResultSet.getObject(5, LocalDateTime.class)) - .isEqualTo(LocalDateTime.of(1970, 1, 1, 0, 0, 0, 10_000_000)); - - // Boolean - assertThat(bigQueryArrowResultSet.getObject("boolField", Boolean.class)).isFalse(); - assertThat(bigQueryArrowResultSet.getObject(1, Boolean.class)).isFalse(); - - // Numbers & Coercions - assertThat(bigQueryArrowResultSet.getObject("int64Filed", Long.class)).isEqualTo(1L); - assertThat(bigQueryArrowResultSet.getObject(2, Integer.class)).isEqualTo(1); - assertThat(bigQueryArrowResultSet.getObject(2, String.class)).isEqualTo("1"); - - assertThat(bigQueryArrowResultSet.getObject("float64Field", Double.class)).isEqualTo(1.1); - - // String - assertThat(bigQueryArrowResultSet.getObject("stringField", String.class)).isEqualTo("text1"); - - // BigDecimal / Numeric - assertThat(bigQueryArrowResultSet.getObject("numericField", BigDecimal.class)) - .isEqualTo(BigDecimal.ONE); - assertThat(bigQueryArrowResultSet.getObject(9, Long.class)).isEqualTo(1L); - - // Unsupported/malformed coercions should fail - assertThrows( - SQLException.class, () -> bigQueryArrowResultSet.getObject("boolField", LocalDate.class)); - assertThrows( - SQLException.class, () -> bigQueryArrowResultSet.getObject("dateField", Boolean.class)); - assertThrows( - SQLException.class, - () -> bigQueryArrowResultSet.getObject("stringField", BigDecimal.class)); + @ParameterizedTest + @MethodSource("failingCoercionCases") + public void testGetObjectWithType_failure(Object column, Class type) throws SQLException { + assertThat(bigQueryArrowResultSet.next()).isTrue(); + if (column instanceof String) { + assertThrows( + SQLException.class, () -> bigQueryArrowResultSet.getObject((String) column, type)); + } else { + assertThrows( + SQLException.class, () -> bigQueryArrowResultSet.getObject((Integer) column, type)); + } } private int resultSetRowCount(BigQueryArrowResultSet resultSet) throws SQLException { diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java index c22fdc7d0048..a4d12adeb60d 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSetTest.java @@ -56,9 +56,13 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; public class BigQueryJsonResultSetTest { @@ -468,58 +472,60 @@ public void testDate() throws SQLException { .isEqualTo(bigQueryJsonResultSet.getDate("fourteenth", calendar).getTime()); } - @Test - public void testGetObjectWithType() throws SQLException { - assertThat(resetResultSet()).isTrue(); - - // java.time types - assertThat(bigQueryJsonResultSet.getObject("fourteenth", LocalDate.class)) - .isEqualTo(LocalDate.of(2020, 1, 15)); - assertThat(bigQueryJsonResultSet.getObject(14, LocalDate.class)) - .isEqualTo(LocalDate.of(2020, 1, 15)); - - assertThat(bigQueryJsonResultSet.getObject("twelfth", LocalTime.class)) - .isEqualTo(LocalTime.of(11, 14, 19, 820000000)); - assertThat(bigQueryJsonResultSet.getObject(12, LocalTime.class)) - .isEqualTo(LocalTime.of(11, 14, 19, 820000000)); - - assertThat(bigQueryJsonResultSet.getObject("fifth", LocalDateTime.class)) - .isEqualTo(LocalDateTime.of(2023, 3, 30, 11, 14, 19, 820000000)); - assertThat(bigQueryJsonResultSet.getObject(5, LocalDateTime.class)) - .isEqualTo(LocalDateTime.of(2023, 3, 30, 11, 14, 19, 820000000)); - - // Boolean - assertThat(bigQueryJsonResultSet.getObject("first", Boolean.class)).isFalse(); - assertThat(bigQueryJsonResultSet.getObject(1, Boolean.class)).isFalse(); - - // Numbers & Coercions - assertThat(bigQueryJsonResultSet.getObject("second", Long.class)).isEqualTo(1L); - assertThat(bigQueryJsonResultSet.getObject(2, Integer.class)).isEqualTo(1); - assertThat(bigQueryJsonResultSet.getObject(2, Short.class)).isEqualTo((short) 1); - assertThat(bigQueryJsonResultSet.getObject(2, String.class)).isEqualTo("1"); - - assertThat(bigQueryJsonResultSet.getObject("third", Double.class)).isEqualTo(1.5D); - assertThat(bigQueryJsonResultSet.getObject(3, Float.class)).isEqualTo(1.5F); + public static Stream successfulCoercionCases() { + return Stream.of( + Arguments.of("fourteenth", LocalDate.class, LocalDate.of(2020, 1, 15)), + Arguments.of(14, LocalDate.class, LocalDate.of(2020, 1, 15)), + Arguments.of("twelfth", LocalTime.class, LocalTime.of(11, 14, 19, 820000000)), + Arguments.of(12, LocalTime.class, LocalTime.of(11, 14, 19, 820000000)), + Arguments.of( + "fifth", LocalDateTime.class, LocalDateTime.of(2023, 3, 30, 11, 14, 19, 820000000)), + Arguments.of(5, LocalDateTime.class, LocalDateTime.of(2023, 3, 30, 11, 14, 19, 820000000)), + Arguments.of("first", Boolean.class, false), + Arguments.of(1, Boolean.class, false), + Arguments.of("second", Long.class, 1L), + Arguments.of(2, Integer.class, 1), + Arguments.of(2, Short.class, (short) 1), + Arguments.of(2, String.class, "1"), + Arguments.of("third", Double.class, 1.5D), + Arguments.of(3, Float.class, 1.5F), + Arguments.of("fourth", String.class, STRING_VAL), + Arguments.of("tenth", BigDecimal.class, new BigDecimal("12345678")), + Arguments.of(10, Long.class, 12345678L), + Arguments.of("eleventh", BigDecimal.class, new BigDecimal("12345678.99")), + Arguments.of(11, Double.class, 12345678.99D)); + } - // String - assertThat(bigQueryJsonResultSet.getObject("fourth", String.class)).isEqualTo(STRING_VAL); + public static Stream failingCoercionCases() { + return Stream.of( + Arguments.of("first", LocalDate.class), + Arguments.of("fourteenth", Boolean.class), + Arguments.of("fourth", BigDecimal.class)); + } - // BigDecimal / Numeric - assertThat(bigQueryJsonResultSet.getObject("tenth", BigDecimal.class)) - .isEqualTo(new BigDecimal("12345678")); - assertThat(bigQueryJsonResultSet.getObject(10, Long.class)).isEqualTo(12345678L); + @ParameterizedTest + @MethodSource("successfulCoercionCases") + public void testGetObjectWithType_success(Object column, Class type, Object expectedValue) + throws SQLException { + assertThat(resetResultSet()).isTrue(); + if (column instanceof String) { + assertThat(bigQueryJsonResultSet.getObject((String) column, type)).isEqualTo(expectedValue); + } else { + assertThat(bigQueryJsonResultSet.getObject((Integer) column, type)).isEqualTo(expectedValue); + } + } - assertThat(bigQueryJsonResultSet.getObject("eleventh", BigDecimal.class)) - .isEqualTo(new BigDecimal("12345678.99")); - assertThat(bigQueryJsonResultSet.getObject(11, Double.class)).isEqualTo(12345678.99D); - - // Unsupported/malformed coercions should fail - assertThrows( - SQLException.class, () -> bigQueryJsonResultSet.getObject("first", LocalDate.class)); - assertThrows( - SQLException.class, () -> bigQueryJsonResultSet.getObject("fourteenth", Boolean.class)); - assertThrows( - SQLException.class, () -> bigQueryJsonResultSet.getObject("fourth", BigDecimal.class)); + @ParameterizedTest + @MethodSource("failingCoercionCases") + public void testGetObjectWithType_failure(Object column, Class type) throws SQLException { + assertThat(resetResultSet()).isTrue(); + if (column instanceof String) { + assertThrows( + SQLException.class, () -> bigQueryJsonResultSet.getObject((String) column, type)); + } else { + assertThrows( + SQLException.class, () -> bigQueryJsonResultSet.getObject((Integer) column, type)); + } } private int resultSetRowCount(BigQueryJsonResultSet resultSet) throws SQLException {