diff --git a/java-bigquery/google-cloud-bigquery-jdbc/.gitignore b/java-bigquery/google-cloud-bigquery-jdbc/.gitignore index 2c4fba513e11..40a3476b598a 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/.gitignore +++ b/java-bigquery/google-cloud-bigquery-jdbc/.gitignore @@ -2,6 +2,7 @@ drivers/** target-it/** *logs*/** **/ITBigQueryJDBCLocalTest.java +**/BigQueryStatementE2EBenchmark.java tools/**/*.class tools/**/*.jfr \ No newline at end of file diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java index 0e72a5fccf71..15397aca93f7 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java @@ -351,6 +351,8 @@ public double getDouble(int columnIndex) throws SQLException { } @Override + @Deprecated + @SuppressWarnings("deprecation") public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { LOG.finest("++enter++"); try { @@ -470,6 +472,8 @@ public InputStream getAsciiStream(int columnIndex) throws SQLException { } @Override + @Deprecated + @SuppressWarnings("deprecation") public InputStream getUnicodeStream(int columnIndex) throws SQLException { LOG.finest("++enter++"); return getInputStream(getString(columnIndex), StandardCharsets.UTF_16LE); @@ -567,6 +571,8 @@ public double getDouble(String columnLabel) throws SQLException { } @Override + @Deprecated + @SuppressWarnings("deprecation") public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { return getBigDecimal(getColumnIndex(columnLabel), scale); } @@ -597,6 +603,8 @@ public InputStream getAsciiStream(String columnLabel) throws SQLException { } @Override + @Deprecated + @SuppressWarnings("deprecation") public InputStream getUnicodeStream(String columnLabel) throws SQLException { return getUnicodeStream(getColumnIndex(columnLabel)); } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryCallableStatement.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryCallableStatement.java index 4c5d6ac3bbcd..260c35f8af1f 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryCallableStatement.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryCallableStatement.java @@ -117,6 +117,8 @@ public BigDecimal getBigDecimal(String arg0) throws SQLException { } @Override + @Deprecated + @SuppressWarnings("deprecation") public BigDecimal getBigDecimal(int arg0, int arg1) throws SQLException { LOG.finest("++enter++"); return getBigDecimal(arg0); diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryPreparedStatement.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryPreparedStatement.java index 356578e8dd27..f02227e0edc3 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryPreparedStatement.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryPreparedStatement.java @@ -233,6 +233,8 @@ public void setAsciiStream(int parameterIndex, InputStream x, int length) { } @Override + @Deprecated + @SuppressWarnings("deprecation") public void setUnicodeStream(int parameterIndex, InputStream x, int length) { // TODO :NOT IMPLEMENTED } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java index ec1ec140975a..ae51736473dd 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeCoercionUtility.java @@ -30,7 +30,6 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Period; -import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; @@ -198,14 +197,12 @@ private static class LongToTime implements BigQueryCoercion { @Override public Time coerce(Long value) { - - int HH = (int) TimeUnit.MICROSECONDS.toHours(value); - int MM = (int) (TimeUnit.MICROSECONDS.toMinutes(value) % 60); - int SS = (int) (TimeUnit.MICROSECONDS.toSeconds(value) % 60); - // Note: BQ Time has a precision of up to six fractional digits (microsecond precision) - // but java.sql.Time do not. So data after seconds is not returned. - return new Time(HH, MM, SS); + // but java.sql.Time only supports up to millisecond precision. So data after milliseconds is + // truncated. + long millisOfDay = value / 1000; + long localMillis = TimeZoneCache.getLocalMillis(millisOfDay); + return new Time(localMillis); } } @@ -214,10 +211,9 @@ private static class LongToTimestamp implements BigQueryCoercion data() { timeZoneRule.enforce(); LocalDateTime aTimeStamp = LocalDateTime.of(2023, MARCH, 30, 11, 14, 19, 820227000); LocalDate aDate = LocalDate.of(2023, MARCH, 30); - LocalTime aTime = LocalTime.of(11, 14, 19, 820227); + LocalTime aTime = LocalTime.of(11, 14, 19, 820227000); return Arrays.asList( new Object[][] { { @@ -176,10 +176,16 @@ STRING, new Text("one"), new Text("two"), new Text("three"), new Text("four")), Long.valueOf("40461820227"), Long.valueOf("40462820227")), new Time[] { - Time.valueOf(aTime), - Time.valueOf(aTime.plusSeconds(1)), - Time.valueOf(aTime.plusSeconds(2)), - Time.valueOf(aTime.plusSeconds(3)) + new Time(java.util.concurrent.TimeUnit.NANOSECONDS.toMillis(aTime.toNanoOfDay())), + new Time( + java.util.concurrent.TimeUnit.NANOSECONDS.toMillis( + aTime.plusSeconds(1).toNanoOfDay())), + new Time( + java.util.concurrent.TimeUnit.NANOSECONDS.toMillis( + aTime.plusSeconds(2).toNanoOfDay())), + new Time( + java.util.concurrent.TimeUnit.NANOSECONDS.toMillis( + aTime.plusSeconds(3).toNanoOfDay())) }, Types.TIME }, diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStructTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStructTest.java index 3f6b528c87db..095435b57fa6 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStructTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStructTest.java @@ -106,7 +106,9 @@ public void structOfPrimitives() throws SQLException { "one", Timestamp.valueOf(LocalDateTime.of(2023, MARCH, 30, 11, 14, 19, 820227000)), Date.valueOf(LocalDate.of(2023, MARCH, 30)), - Time.valueOf(LocalTime.of(11, 14, 19, 820227)), + new Time( + java.util.concurrent.TimeUnit.NANOSECONDS.toMillis( + LocalTime.of(11, 14, 19, 820227000).toNanoOfDay())), Timestamp.valueOf("2023-03-30 11:14:19.820227"), "POINT(-122 47)", "one".getBytes()) @@ -117,7 +119,7 @@ public void structOfPrimitives() throws SQLException { public void structOfArrays() throws SQLException { LocalDateTime aTimeStamp = LocalDateTime.of(2023, MARCH, 30, 11, 14, 19, 820227000); LocalDate aDate = LocalDate.of(2023, MARCH, 30); - LocalTime aTime = LocalTime.of(11, 14, 19, 820227); + LocalTime aTime = LocalTime.of(11, 14, 19, 820227000); List>> schemaAndValues = Arrays.asList( arrowArraySchemaAndValue(INT64, 10L, 20L), @@ -165,7 +167,13 @@ GEOGRAPHY, new Text("POINT(-122 47)"), new Text("POINT(-122 48)")), assertThat(((Array) attributes[7]).getArray()) .isEqualTo(new Date[] {Date.valueOf(aDate), Date.valueOf(aDate.plusDays(1))}); assertThat(((Array) attributes[8]).getArray()) - .isEqualTo(new Time[] {Time.valueOf(aTime), Time.valueOf(aTime.plusSeconds(1))}); + .isEqualTo( + new Time[] { + new Time(java.util.concurrent.TimeUnit.NANOSECONDS.toMillis(aTime.toNanoOfDay())), + new Time( + java.util.concurrent.TimeUnit.NANOSECONDS.toMillis( + aTime.plusSeconds(1).toNanoOfDay())) + }); assertThat(((Array) attributes[9]).getArray()) // DATETIME .isEqualTo( new Timestamp[] { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/FieldValueTypeBigQueryCoercionUtilityTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/FieldValueTypeBigQueryCoercionUtilityTest.java index b5df1e1fc58c..594004c8ff95 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/FieldValueTypeBigQueryCoercionUtilityTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/FieldValueTypeBigQueryCoercionUtilityTest.java @@ -29,6 +29,7 @@ import com.google.cloud.bigquery.FieldValueList; import com.google.cloud.bigquery.Range; import com.google.cloud.bigquery.exception.BigQueryJdbcCoercionException; +import com.google.cloud.bigquery.jdbc.rules.TimeZoneRule; import com.google.common.collect.ImmutableList; import java.math.BigDecimal; import java.sql.Date; @@ -36,14 +37,14 @@ import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.ZoneId; import java.time.temporal.ChronoUnit; -import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; public class FieldValueTypeBigQueryCoercionUtilityTest { + @RegisterExtension public final TimeZoneRule timeZoneRule = new TimeZoneRule("UTC"); + private static final FieldValue STRING_VALUE = FieldValue.of(PRIMITIVE, "sample-string"); private static final FieldValue INTEGER_VALUE = FieldValue.of(PRIMITIVE, "345"); private static final FieldValue FLOAT_VALUE = FieldValue.of(PRIMITIVE, "345.21"); @@ -299,9 +300,8 @@ public void fieldValueToBytesArrayWhenInnerValueIsNull() { @Test public void fieldValueToTimestamp() { Instant instant = Instant.EPOCH.plus(TIMESTAMP_VALUE.getTimestampValue(), ChronoUnit.MICROS); - LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC")); assertThat(INSTANCE.coerceTo(Timestamp.class, TIMESTAMP_VALUE)) - .isEqualTo(Timestamp.valueOf(localDateTime)); + .isEqualTo(Timestamp.from(instant)); } @Test @@ -317,11 +317,28 @@ public void fieldValueToTimestampWhenInnerValueIsNull() { @Test public void fieldValueToTime() { LocalTime expectedTime = LocalTime.of(23, 59, 59); - assertThat(INSTANCE.coerceTo(Time.class, TIME_VALUE)) - .isEqualTo(new Time(TimeUnit.NANOSECONDS.toMillis(expectedTime.toNanoOfDay()))); - LocalTime expectedTimeWithNanos = LocalTime.parse("23:59:59.99999"); + assertThat(INSTANCE.coerceTo(Time.class, TIME_VALUE)).isEqualTo(Time.valueOf(expectedTime)); + // expectedTimeWithNanos has 999 milliseconds, giving 86399999 ms of day. + // Since the test runs under UTC timezone by TimeZoneRule, expected localMillis is 86399999L. assertThat(INSTANCE.coerceTo(Time.class, TIME_WITH_NANOSECOND_VALUE)) - .isEqualTo(new Time(TimeUnit.NANOSECONDS.toMillis(expectedTimeWithNanos.toNanoOfDay()))); + .isEqualTo(new Time(86399999L)); + } + + @Test + public void fieldValueToTimeInNonUTCTimeZone() { + java.util.TimeZone originalTimeZone = java.util.TimeZone.getDefault(); + try { + java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("America/Los_Angeles")); + com.google.cloud.bigquery.jdbc.TimeZoneCache.reset(); + // 23:59:59.99999 yields 86399999 milliseconds. + // Under America/Los_Angeles on 1970-01-01 (PST, -8 hours offset), + // the subtracted offset results in 86399999 - (-28800000) = 115199999L. + assertThat(INSTANCE.coerceTo(Time.class, TIME_WITH_NANOSECOND_VALUE)) + .isEqualTo(new Time(115199999L)); + } finally { + java.util.TimeZone.setDefault(originalTimeZone); + com.google.cloud.bigquery.jdbc.TimeZoneCache.reset(); + } } @Test diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/rules/TimeZoneRule.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/rules/TimeZoneRule.java index 3a1c54a34ff4..18ca9f2b34d9 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/rules/TimeZoneRule.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/rules/TimeZoneRule.java @@ -37,11 +37,13 @@ public TimeZoneRule(String timeZoneId) { public void beforeAll(ExtensionContext context) { defaultTimeZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone(timeZoneId)); + com.google.cloud.bigquery.jdbc.TimeZoneCache.reset(); } @Override public void afterAll(ExtensionContext context) { TimeZone.setDefault(defaultTimeZone); + com.google.cloud.bigquery.jdbc.TimeZoneCache.reset(); } @Override @@ -50,6 +52,7 @@ public void beforeEach(ExtensionContext context) { defaultTimeZone = TimeZone.getDefault(); } TimeZone.setDefault(TimeZone.getTimeZone(timeZoneId)); + com.google.cloud.bigquery.jdbc.TimeZoneCache.reset(); } @Override @@ -57,10 +60,12 @@ public void afterEach(ExtensionContext context) { if (defaultTimeZone != null) { TimeZone.setDefault(defaultTimeZone); } + com.google.cloud.bigquery.jdbc.TimeZoneCache.reset(); } /** Public method to enforce the rule manually */ public void enforce() { TimeZone.setDefault(TimeZone.getTimeZone(timeZoneId)); + com.google.cloud.bigquery.jdbc.TimeZoneCache.reset(); } }