diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index fc078dbe3f..be7c5c0fe1 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -16,6 +16,7 @@ branchProtectionRules: - 'Kokoro - Test: Integration' - cla/google - javadoc + - unmanaged_dependency_check - pattern: 1.127.12-sp isAdminEnforced: true requiredApprovingReviewCount: 1 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 1be1c2a32a..1c8355fdac 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@6a89f57882288b3d2f190cda65000eec9e9ebb7c # v2.27.0 + uses: github/codeql-action/upload-sarif@d835c34a7026e284170c41a0a66c956e03f247d0 # v2.27.7 with: sarif_file: results.sarif diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml index 4df9466e7d..f971b05c95 100644 --- a/.github/workflows/unmanaged_dependency_check.yaml +++ b/.github/workflows/unmanaged_dependency_check.yaml @@ -17,7 +17,7 @@ jobs: # repository .kokoro/build.sh - name: Unmanaged dependency check - uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.39.0 + uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.41.0 with: # java-bigquery does not produce a BOM. Fortunately the root pom.xml # defines google-cloud-bigquery in dependencyManagement section. So diff --git a/.kokoro/continuous/graalvm-native-17.cfg b/.kokoro/continuous/graalvm-native-17.cfg index 4bc28d0afa..cbccd336a0 100644 --- a/.kokoro/continuous/graalvm-native-17.cfg +++ b/.kokoro/continuous/graalvm-native-17.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.39.0" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.41.0" } env_vars: { diff --git a/.kokoro/continuous/graalvm-native.cfg b/.kokoro/continuous/graalvm-native.cfg index 33a4a9d957..d9c9d02051 100644 --- a/.kokoro/continuous/graalvm-native.cfg +++ b/.kokoro/continuous/graalvm-native.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.39.0" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.41.0" } env_vars: { diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-17.cfg index ba53eb9ce2..80a629d235 100644 --- a/.kokoro/presubmit/graalvm-native-17.cfg +++ b/.kokoro/presubmit/graalvm-native-17.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.39.0"" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.41.0"" } env_vars: { diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native.cfg index 418a399b75..644bb62ce7 100644 --- a/.kokoro/presubmit/graalvm-native.cfg +++ b/.kokoro/presubmit/graalvm-native.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.39.0" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.41.0" } env_vars: { diff --git a/CHANGELOG.md b/CHANGELOG.md index ac6b1d9e93..9d8f525e2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,63 @@ # Changelog +## [2.45.0](https://github.com/googleapis/java-bigquery/compare/v2.44.0...v2.45.0) (2024-12-13) + + +### Features + +* Enable Lossless Timestamps in BQ java client lib ([#3589](https://github.com/googleapis/java-bigquery/issues/3589)) ([c0b874a](https://github.com/googleapis/java-bigquery/commit/c0b874aa0150e63908450b13d019864b8cbfbfe3)) +* Introduce `java.time` methods and variables ([#3586](https://github.com/googleapis/java-bigquery/issues/3586)) ([31fb15f](https://github.com/googleapis/java-bigquery/commit/31fb15fb963c18e4c29391e9fe56dfde31577511)) + + +### Bug Fixes + +* **test:** Update schema for broken ConnImplBenchmark test ([#3574](https://github.com/googleapis/java-bigquery/issues/3574)) ([8cf4387](https://github.com/googleapis/java-bigquery/commit/8cf4387fae22c81d40635b470b216fa4c126d681)) + + +### Dependencies + +* Update dependency com.google.api.grpc:proto-google-cloud-bigqueryconnection-v1 to v2.56.0 ([#3582](https://github.com/googleapis/java-bigquery/issues/3582)) ([616ee2a](https://github.com/googleapis/java-bigquery/commit/616ee2aa8ccf3d2975274b256252f2f249775960)) +* Update dependency com.google.apis:google-api-services-bigquery to v2-rev20241111-2.0.0 ([#3591](https://github.com/googleapis/java-bigquery/issues/3591)) ([3eef3a9](https://github.com/googleapis/java-bigquery/commit/3eef3a9959bcfdb76c26fdf9069d9acf89f93a7a)) +* Update dependency com.google.apis:google-api-services-bigquery to v2-rev20241115-2.0.0 ([#3601](https://github.com/googleapis/java-bigquery/issues/3601)) ([41f9adb](https://github.com/googleapis/java-bigquery/commit/41f9adbe4235329fa2bbfd0930f4113e63f72e05)) +* Update dependency com.google.cloud:google-cloud-datacatalog-bom to v1.60.0 ([#3583](https://github.com/googleapis/java-bigquery/issues/3583)) ([34dd8bc](https://github.com/googleapis/java-bigquery/commit/34dd8bc22c8188f2b61dc9939b24a8d820548e2b)) +* Update dependency com.google.cloud:sdk-platform-java-config to v3.41.0 ([#3607](https://github.com/googleapis/java-bigquery/issues/3607)) ([11499d1](https://github.com/googleapis/java-bigquery/commit/11499d16727934fd3dfa5c18226e6f20471a11ac)) +* Update github/codeql-action action to v2.27.5 ([#3588](https://github.com/googleapis/java-bigquery/issues/3588)) ([3f94075](https://github.com/googleapis/java-bigquery/commit/3f9407570fea5317aaf212b058ca1da05985eda9)) +* Update github/codeql-action action to v2.27.6 ([#3597](https://github.com/googleapis/java-bigquery/issues/3597)) ([bc1f3b9](https://github.com/googleapis/java-bigquery/commit/bc1f3b97a0c8ccc6e93a07b2f0ebcf8e05da9b48)) +* Update github/codeql-action action to v2.27.7 ([#3603](https://github.com/googleapis/java-bigquery/issues/3603)) ([528426b](https://github.com/googleapis/java-bigquery/commit/528426bf9b7801b1b9b45758b560f14a4c9bbc57)) + + +### Documentation + +* **bigquery:** Add javadoc description of timestamp() parameter. ([#3604](https://github.com/googleapis/java-bigquery/issues/3604)) ([6ee0c10](https://github.com/googleapis/java-bigquery/commit/6ee0c103771ef678f66cc7a584bdce27e21f29c4)) + +## [2.44.0](https://github.com/googleapis/java-bigquery/compare/v2.43.3...v2.44.0) (2024-11-17) + + +### Features + +* Enable maxTimeTravelHours in BigQuery java client library ([#3555](https://github.com/googleapis/java-bigquery/issues/3555)) ([bd24fd8](https://github.com/googleapis/java-bigquery/commit/bd24fd8c550bfbd1207b194ed5c863a4a9924d48)) + + +### Bug Fixes + +* Update experimental methods documentation to [@internalapi](https://github.com/internalapi) ([#3552](https://github.com/googleapis/java-bigquery/issues/3552)) ([20826f1](https://github.com/googleapis/java-bigquery/commit/20826f1b08a3cc5bdcce5637b7ea21d467b2bce2)) + + +### Dependencies + +* Update dependency com.google.api.grpc:proto-google-cloud-bigqueryconnection-v1 to v2.55.0 ([#3559](https://github.com/googleapis/java-bigquery/issues/3559)) ([950ad0c](https://github.com/googleapis/java-bigquery/commit/950ad0cce6370e332a568d3b2e9ef3911503d206)) +* Update dependency com.google.apis:google-api-services-bigquery to v2-rev20241027-2.0.0 ([#3568](https://github.com/googleapis/java-bigquery/issues/3568)) ([b5ccfcc](https://github.com/googleapis/java-bigquery/commit/b5ccfccb552e731ccb09be923715849a4282d44d)) +* Update dependency com.google.cloud:google-cloud-datacatalog-bom to v1.59.0 ([#3561](https://github.com/googleapis/java-bigquery/issues/3561)) ([1bd24a1](https://github.com/googleapis/java-bigquery/commit/1bd24a1ad28d168587b7cba95ec348cb1308a803)) +* Update dependency com.google.cloud:sdk-platform-java-config to v3.40.0 ([#3576](https://github.com/googleapis/java-bigquery/issues/3576)) ([d5fa951](https://github.com/googleapis/java-bigquery/commit/d5fa951b8255ec1bcbcdf9bb3c29f247e38a0c7e)) +* Update github/codeql-action action to v2.27.1 ([#3567](https://github.com/googleapis/java-bigquery/issues/3567)) ([e154ee3](https://github.com/googleapis/java-bigquery/commit/e154ee300485dc9d900343a8b5ceb7f6633bc3ff)) +* Update github/codeql-action action to v2.27.3 ([#3569](https://github.com/googleapis/java-bigquery/issues/3569)) ([3707a40](https://github.com/googleapis/java-bigquery/commit/3707a402039365c49e1976a388593f621231dc02)) +* Update github/codeql-action action to v2.27.4 ([#3572](https://github.com/googleapis/java-bigquery/issues/3572)) ([2c7b4f7](https://github.com/googleapis/java-bigquery/commit/2c7b4f750f4c8bf03c0ba74402d745341382a209)) + + +### Documentation + +* Fix BigQuery documentation formating ([#3565](https://github.com/googleapis/java-bigquery/issues/3565)) ([552f491](https://github.com/googleapis/java-bigquery/commit/552f49132af370f66aa1ccdde86e6280f638da22)) + ## [2.43.3](https://github.com/googleapis/java-bigquery/compare/v2.43.2...v2.43.3) (2024-10-29) diff --git a/benchmark/pom.xml b/benchmark/pom.xml index a371796e0d..b6daf595e9 100644 --- a/benchmark/pom.xml +++ b/benchmark/pom.xml @@ -6,7 +6,7 @@ google-cloud-bigquery-parent com.google.cloud - 2.43.3 + 2.45.0 diff --git a/benchmark/src/main/java/com.google.cloud.bigquery/ConnImplBenchmark.java b/benchmark/src/main/java/com.google.cloud.bigquery/ConnImplBenchmark.java index f202df6669..670e31ff6e 100644 --- a/benchmark/src/main/java/com.google.cloud.bigquery/ConnImplBenchmark.java +++ b/benchmark/src/main/java/com.google.cloud.bigquery/ConnImplBenchmark.java @@ -20,6 +20,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.logging.Level; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -48,31 +49,19 @@ public class ConnImplBenchmark { public int rowLimit; private ConnectionSettings connectionSettingsReadAPIEnabled, connectionSettingsReadAPIDisabled; - private long numBuffRows = 100000L; - private final String DATASET = "new_york_taxi_trips"; private final String QUERY = "SELECT * FROM bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2017 LIMIT %s"; - public static final long NUM_PAGE_ROW_CNT_RATIO = - 10; // ratio of [records in the current page :: total rows] to be met to use read API - public static final long NUM_MIN_RESULT_SIZE = - 200000; // min number of records to use to ReadAPI with @Setup public void setUp() throws IOException { java.util.logging.Logger.getGlobal().setLevel(Level.ALL); - connectionSettingsReadAPIEnabled = - ConnectionSettings.newBuilder() - .setUseReadAPI(true) // enable read api - .build(); + connectionSettingsReadAPIEnabled = ConnectionSettings.newBuilder().setUseReadAPI(true).build(); connectionSettingsReadAPIDisabled = - ConnectionSettings.newBuilder() - .setUseReadAPI(false) // disable read api - .build(); + ConnectionSettings.newBuilder().setUseReadAPI(false).build(); } @Benchmark - // uses bigquery.query public void iterateRecordsWithBigQuery_Query(Blackhole blackhole) throws InterruptedException { String selectQuery = String.format(QUERY, rowLimit); BigQuery bigQuery = BigQueryOptions.getDefaultInstance().getService(); @@ -81,81 +70,35 @@ public void iterateRecordsWithBigQuery_Query(Blackhole blackhole) throws Interru TableResult result = bigQuery.query(config); long hash = 0L; int cnt = 0; - System.out.print("\n Running"); - // iterate al the records and compute the hash + long lastTime = System.currentTimeMillis(); + System.out.println("\n Running"); for (FieldValueList row : result.iterateAll()) { - hash += - row.get("vendor_id").getStringValue() == null - ? 0 - : row.get("vendor_id").getStringValue().hashCode(); - hash += - row.get("pickup_datetime").getStringValue() == null - ? 0 - : row.get("pickup_datetime").getStringValue().hashCode(); - hash += - row.get("dropoff_datetime").getStringValue() == null - ? 0 - : row.get("dropoff_datetime").getStringValue().hashCode(); - hash += - row.get("passenger_count").getValue() == null - ? 0 - : row.get("passenger_count").getLongValue(); - hash += - row.get("trip_distance").getValue() == null - ? 0 - : row.get("trip_distance").getDoubleValue(); - hash += - row.get("pickup_longitude").getValue() == null - ? 0 - : row.get("pickup_longitude").getDoubleValue(); - hash += - row.get("pickup_latitude").getValue() == null - ? 0 - : row.get("pickup_latitude").getDoubleValue(); - hash += - row.get("rate_code").getStringValue() == null - ? 0 - : row.get("rate_code").getStringValue().hashCode(); - hash += - row.get("store_and_fwd_flag").getStringValue() == null - ? 0 - : row.get("store_and_fwd_flag").getStringValue().hashCode(); - hash += - row.get("payment_type").getStringValue() == null - ? 0 - : row.get("payment_type").getStringValue().hashCode(); - hash += - row.get("pickup_location_id").getStringValue() == null - ? 0 - : row.get("pickup_location_id").getStringValue().hashCode(); - hash += - row.get("dropoff_location_id").getStringValue() == null - ? 0 - : row.get("dropoff_location_id").getStringValue().hashCode(); - hash += - row.get("dropoff_longitude").getValue() == null - ? 0 - : row.get("dropoff_longitude").getDoubleValue(); - hash += - row.get("dropoff_latitude").getValue() == null - ? 0 - : row.get("dropoff_latitude").getDoubleValue(); - hash += - row.get("fare_amount").getValue() == null ? 0 : row.get("fare_amount").getDoubleValue(); - hash += row.get("extra").getValue() == null ? 0 : row.get("extra").getDoubleValue(); - hash += row.get("mta_tax").getValue() == null ? 0 : row.get("mta_tax").getDoubleValue(); - hash += row.get("tip_amount").getValue() == null ? 0 : row.get("tip_amount").getDoubleValue(); - hash += - row.get("tolls_amount").getValue() == null ? 0 : row.get("tolls_amount").getDoubleValue(); - hash += - row.get("imp_surcharge").getValue() == null - ? 0 - : row.get("imp_surcharge").getDoubleValue(); - hash += - row.get("total_amount").getValue() == null ? 0 : row.get("total_amount").getDoubleValue(); + hash += computeHash(row.get("vendor_id"), FieldValue::getStringValue); + hash += computeHash(row.get("pickup_datetime"), FieldValue::getStringValue); + hash += computeHash(row.get("dropoff_datetime"), FieldValue::getStringValue); + hash += computeHash(row.get("passenger_count"), FieldValue::getLongValue); + hash += computeHash(row.get("trip_distance"), FieldValue::getDoubleValue); + hash += computeHash(row.get("rate_code"), FieldValue::getStringValue); + hash += computeHash(row.get("store_and_fwd_flag"), FieldValue::getStringValue); + hash += computeHash(row.get("payment_type"), FieldValue::getStringValue); + hash += computeHash(row.get("fare_amount"), FieldValue::getDoubleValue); + hash += computeHash(row.get("extra"), FieldValue::getDoubleValue); + hash += computeHash(row.get("mta_tax"), FieldValue::getDoubleValue); + hash += computeHash(row.get("tip_amount"), FieldValue::getDoubleValue); + hash += computeHash(row.get("tolls_amount"), FieldValue::getDoubleValue); + hash += computeHash(row.get("imp_surcharge"), FieldValue::getDoubleValue); + hash += computeHash(row.get("airport_fee"), FieldValue::getDoubleValue); + hash += computeHash(row.get("total_amount"), FieldValue::getDoubleValue); + hash += computeHash(row.get("pickup_location_id"), FieldValue::getStringValue); + hash += computeHash(row.get("dropoff_location_id"), FieldValue::getStringValue); + hash += computeHash(row.get("data_file_year"), FieldValue::getLongValue); + hash += computeHash(row.get("data_file_month"), FieldValue::getLongValue); - if (++cnt % 100000 == 0) { // just to indicate the progress while long running benchmarks - System.out.print("."); + if (++cnt % 100_000 == 0) { + long now = System.currentTimeMillis(); + long duration = now - lastTime; + System.out.println("ROW " + cnt + " Time: " + duration + " ms"); + lastTime = now; } } System.out.println(cnt + " records processed using bigquery.query"); @@ -202,54 +145,67 @@ public void iterateRecordsWithoutUsingReadAPI(Blackhole blackhole) blackhole.consume(hash); } - // Hashes all the 20 columns of all the rows private long getResultHash(BigQueryResult bigQueryResultSet) throws SQLException { ResultSet rs = bigQueryResultSet.getResultSet(); long hash = 0L; int cnt = 0; - System.out.print("\n Running"); + long lastTime = System.currentTimeMillis(); + System.out.println("\n Running"); while (rs.next()) { - hash += rs.getString("vendor_id") == null ? 0 : rs.getString("vendor_id").hashCode(); - hash += - rs.getString("pickup_datetime") == null ? 0 : rs.getString("pickup_datetime").hashCode(); - hash += - rs.getString("dropoff_datetime") == null - ? 0 - : rs.getString("dropoff_datetime").hashCode(); - hash += rs.getLong("passenger_count"); - hash += rs.getDouble("trip_distance"); - hash += rs.getDouble("pickup_longitude"); - hash += rs.getDouble("pickup_latitude"); - hash += rs.getString("rate_code") == null ? 0 : rs.getString("rate_code").hashCode(); - hash += - rs.getString("store_and_fwd_flag") == null - ? 0 - : rs.getString("store_and_fwd_flag").hashCode(); - hash += rs.getDouble("dropoff_longitude"); - hash += rs.getDouble("dropoff_latitude"); - hash += rs.getString("payment_type") == null ? 0 : rs.getString("payment_type").hashCode(); - hash += rs.getDouble("fare_amount"); - hash += rs.getDouble("extra"); - hash += rs.getDouble("mta_tax"); - hash += rs.getDouble("tip_amount"); - hash += rs.getDouble("tolls_amount"); - hash += rs.getDouble("imp_surcharge"); - hash += rs.getDouble("total_amount"); - hash += - rs.getString("pickup_location_id") == null - ? 0 - : rs.getString("pickup_location_id").hashCode(); - hash += - rs.getString("dropoff_location_id") == null - ? 0 - : rs.getString("dropoff_location_id").hashCode(); - if (++cnt % 100000 == 0) { // just to indicate the progress while long running benchmarks - System.out.print("."); + hash += computeHash(rs, "vendor_id", ResultSet::getString); + hash += computeHash(rs, "pickup_datetime", ResultSet::getString); + hash += computeHash(rs, "dropoff_datetime", ResultSet::getString); + hash += computeHash(rs, "passenger_count", ResultSet::getLong); + hash += computeHash(rs, "trip_distance", ResultSet::getDouble); + hash += computeHash(rs, "rate_code", ResultSet::getString); + hash += computeHash(rs, "store_and_fwd_flag", ResultSet::getString); + hash += computeHash(rs, "payment_type", ResultSet::getString); + hash += computeHash(rs, "fare_amount", ResultSet::getDouble); + hash += computeHash(rs, "extra", ResultSet::getDouble); + hash += computeHash(rs, "mta_tax", ResultSet::getDouble); + hash += computeHash(rs, "tip_amount", ResultSet::getDouble); + hash += computeHash(rs, "tolls_amount", ResultSet::getDouble); + hash += computeHash(rs, "imp_surcharge", ResultSet::getDouble); + hash += computeHash(rs, "airport_fee", ResultSet::getDouble); + hash += computeHash(rs, "total_amount", ResultSet::getDouble); + hash += computeHash(rs, "pickup_location_id", ResultSet::getString); + hash += computeHash(rs, "dropoff_location_id", ResultSet::getString); + hash += computeHash(rs, "data_file_year", ResultSet::getLong); + hash += computeHash(rs, "data_file_month", ResultSet::getLong); + + if (++cnt % 100_000 == 0) { + long now = System.currentTimeMillis(); + long duration = now - lastTime; + System.out.println("ROW " + cnt + " Time: " + duration + " ms"); + lastTime = now; } } return hash; } + private long computeHash( + ResultSet rs, String columnName, SQLFunction extractor) { + try { + T value = extractor.apply(rs, columnName); + return (value == null) ? 0 : value.hashCode(); + } catch (SQLException e) { + return 0; + } + } + + @FunctionalInterface + private interface SQLFunction { + R apply(T t, String columnName) throws SQLException; + } + + private long computeHash(FieldValue fieldValue, Function extractor) { + if (fieldValue == null || fieldValue.isNull()) { + return 0; + } + T value = extractor.apply(fieldValue); + return (value == null) ? 0 : value.hashCode(); + } + public static void main(String[] args) throws Exception { Options opt = new OptionsBuilder().include(ConnImplBenchmark.class.getSimpleName()).build(); new Runner(opt).run(); diff --git a/google-cloud-bigquery-bom/pom.xml b/google-cloud-bigquery-bom/pom.xml index c52bc5d4b7..0a689e33f8 100644 --- a/google-cloud-bigquery-bom/pom.xml +++ b/google-cloud-bigquery-bom/pom.xml @@ -3,12 +3,12 @@ 4.0.0 com.google.cloud google-cloud-bigquery-bom - 2.43.3 + 2.45.0 pom com.google.cloud sdk-platform-java-config - 3.39.0 + 3.41.0 @@ -54,7 +54,7 @@ com.google.cloud google-cloud-bigquery - 2.43.3 + 2.45.0 diff --git a/google-cloud-bigquery/clirr-ignored-differences.xml b/google-cloud-bigquery/clirr-ignored-differences.xml index 9c69fd6a68..b6546847fe 100644 --- a/google-cloud-bigquery/clirr-ignored-differences.xml +++ b/google-cloud-bigquery/clirr-ignored-differences.xml @@ -129,4 +129,9 @@ com/google/cloud/bigquery/StandardTableDefinition* *BigLakeConfiguration(*) + + 7013 + com/google/cloud/bigquery/DatasetInfo* + *setMaxTimeTravelHours(*) + \ No newline at end of file diff --git a/google-cloud-bigquery/pom.xml b/google-cloud-bigquery/pom.xml index 52538ce4f0..c040d4fc37 100644 --- a/google-cloud-bigquery/pom.xml +++ b/google-cloud-bigquery/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-bigquery - 2.43.3 + 2.45.0 jar BigQuery https://github.com/googleapis/java-bigquery @@ -11,7 +11,7 @@ com.google.cloud google-cloud-bigquery-parent - 2.43.3 + 2.45.0 google-cloud-bigquery @@ -85,10 +85,6 @@ com.google.api gax - - org.threeten - threetenbp - com.google.code.gson gson diff --git a/google-cloud-bigquery/src/benchmark/java/com/google/cloud/bigquery/benchmark/Benchmark.java b/google-cloud-bigquery/src/benchmark/java/com/google/cloud/bigquery/benchmark/Benchmark.java index caec16676f..9d05f6b2b4 100644 --- a/google-cloud-bigquery/src/benchmark/java/com/google/cloud/bigquery/benchmark/Benchmark.java +++ b/google-cloud-bigquery/src/benchmark/java/com/google/cloud/bigquery/benchmark/Benchmark.java @@ -23,10 +23,10 @@ import com.google.cloud.bigquery.QueryJobConfiguration; import com.google.cloud.bigquery.TableResult; import java.io.FileInputStream; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; import java.util.List; -import org.threeten.bp.Clock; -import org.threeten.bp.Duration; -import org.threeten.bp.Instant; public class Benchmark { diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQuery.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQuery.java index 675e6c1e86..613134fa06 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQuery.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQuery.java @@ -244,8 +244,9 @@ private DatasetListOption(BigQueryRpc.Option option, Object value) { } /** - * Returns an option to specify a label filter. See - * https://cloud.google.com/bigquery/docs/adding-using-labels#filtering_datasets_using_labels + * Returns an option to specify a label filter. @see Filtering + * using labels * * @param labelFilter In the form "labels.key:value" */ @@ -280,7 +281,7 @@ private DatasetOption(BigQueryRpc.Option option, Object value) { /** * Returns an option to specify the dataset's fields to be returned by the RPC call. If this - * option is not provided all dataset's fields are returned. { code DatasetOption.fields} can be + * option is not provided all dataset's fields are returned. {@code DatasetOption.fields} can be * used to specify only the fields of interest. {@link Dataset#getDatasetId()} is always * returned, even if not specified. */ @@ -864,8 +865,7 @@ public int hashCode() { /** * Lists the project's datasets. This method returns partial information on each dataset: ({@link * Dataset#getDatasetId()}, {@link Dataset#getFriendlyName()} and {@link - * Dataset#getGeneratedId()}). To get complete information use either {@link #getDataset(String, - * DatasetOption...)} or {@link #getDataset(DatasetId, DatasetOption...)}. + * Dataset#getGeneratedId()}). To get complete information use {@link #getDataset}. * *

Example of listing datasets, specifying the page size. * @@ -884,8 +884,7 @@ public int hashCode() { /** * Lists the datasets in the provided project. This method returns partial information on each * dataset: ({@link Dataset#getDatasetId()}, {@link Dataset#getFriendlyName()} and {@link - * Dataset#getGeneratedId()}). To get complete information use either {@link #getDataset(String, - * DatasetOption...)} or {@link #getDataset(DatasetId, DatasetOption...)}. + * Dataset#getGeneratedId()}). To get complete information use either {@link #getDataset}. * *

Example of listing datasets in a project, specifying the page size. * @@ -1205,7 +1204,7 @@ public int hashCode() { * Lists the tables in the dataset. This method returns partial information on each table: ({@link * Table#getTableId()}, {@link Table#getFriendlyName()}, {@link Table#getGeneratedId()} and type, * which is part of {@link Table#getDefinition()}). To get complete information use either {@link - * #getTable(TableId, TableOption...)} or {@link #getTable(String, String, TableOption...)}. + * #getTable}. * *

Example of listing the tables in a dataset, specifying the page size. * @@ -1225,7 +1224,7 @@ public int hashCode() { * Lists the tables in the dataset. This method returns partial information on each table: ({@link * Table#getTableId()}, {@link Table#getFriendlyName()}, {@link Table#getGeneratedId()} and type, * which is part of {@link Table#getDefinition()}). To get complete information use either {@link - * #getTable(TableId, TableOption...)} or {@link #getTable(String, String, TableOption...)}. + * #getTable}. * *

Example of listing the tables in a dataset. * @@ -1429,8 +1428,7 @@ TableResult listTableData( /** * Sends a job cancel request. This call will return immediately. The job status can then be - * checked using either {@link #getJob(JobId, JobOption...)} or {@link #getJob(String, - * JobOption...)}). + * checked by using {@link #getJob}. * *

If the location of the job is not "US" or "EU", {@link #cancel(JobId)} must be used instead. * @@ -1454,8 +1452,7 @@ TableResult listTableData( /** * Sends a job cancel request. This call will return immediately. The job status can then be - * checked using either {@link #getJob(JobId, JobOption...)} or {@link #getJob(String, - * JobOption...)}). + * checked using {@link #getJob}. * *

If the location of the job is not "US" or "EU", the {@code jobId} must specify the job * location. diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java index 5760832157..23cb001ac2 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java @@ -60,8 +60,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.checkerframework.checker.nullness.qual.NonNull; -import org.threeten.bp.Instant; -import org.threeten.bp.temporal.ChronoUnit; final class BigQueryImpl extends BaseService implements BigQuery { @@ -450,7 +448,9 @@ public com.google.api.services.bigquery.model.Job call() { long jobCreationTime = job.getStatistics().getCreationTime(); long jobMinStaleTime = System.currentTimeMillis(); long jobMaxStaleTime = - Instant.ofEpochMilli(jobMinStaleTime).minus(1, ChronoUnit.DAYS).toEpochMilli(); + java.time.Instant.ofEpochMilli(jobMinStaleTime) + .minus(1, java.time.temporal.ChronoUnit.DAYS) + .toEpochMilli(); // Only return the job if it has been created in the past 24 hours. // This is assuming any job older than 24 hours is a valid duplicate JobID @@ -1206,7 +1206,7 @@ public TableDataList call() { new PageImpl<>( new TableDataPageFetcher(tableId, schema, serviceOptions, cursor, pageOptionMap), cursor, - transformTableData(result.getRows(), schema)), + transformTableData(result.getRows(), schema, serviceOptions.getUseInt64Timestamps())), result.getTotalRows()); } catch (RetryHelper.RetryHelperException e) { throw BigQueryException.translateAndThrow(e); @@ -1214,7 +1214,7 @@ public TableDataList call() { } private static Iterable transformTableData( - Iterable tableDataPb, final Schema schema) { + Iterable tableDataPb, final Schema schema, boolean useInt64Timestamps) { return ImmutableList.copyOf( Iterables.transform( tableDataPb != null ? tableDataPb : ImmutableList.of(), @@ -1223,7 +1223,7 @@ private static Iterable transformTableData( @Override public FieldValueList apply(TableRow rowPb) { - return FieldValueList.fromPb(rowPb.getF(), fields); + return FieldValueList.fromPb(rowPb.getF(), fields, useInt64Timestamps); } })); } @@ -1347,7 +1347,8 @@ public TableResult query(QueryJobConfiguration configuration, JobOption... optio // If all parameters passed in configuration are supported by the query() method on the backend, // put on fast path - QueryRequestInfo requestInfo = new QueryRequestInfo(configuration); + QueryRequestInfo requestInfo = + new QueryRequestInfo(configuration, getOptions().getUseInt64Timestamps()); if (requestInfo.isFastQuerySupported(null)) { String projectId = getOptions().getProjectId(); QueryRequest content = requestInfo.toPb(); @@ -1420,7 +1421,8 @@ public com.google.api.services.bigquery.model.QueryResponse call() { // fetch next pages of results new QueryPageFetcher(jobId, schema, getOptions(), cursor, optionMap(options)), cursor, - transformTableData(results.getRows(), schema))) + transformTableData( + results.getRows(), schema, getOptions().getUseInt64Timestamps()))) .setJobId(jobId) .setQueryId(results.getQueryId()) .build(); @@ -1433,7 +1435,8 @@ public com.google.api.services.bigquery.model.QueryResponse call() { new PageImpl<>( new TableDataPageFetcher(null, schema, getOptions(), null, optionMap(options)), null, - transformTableData(results.getRows(), schema))) + transformTableData( + results.getRows(), schema, getOptions().getUseInt64Timestamps()))) // Return the JobID of the successful job .setJobId( results.getJobReference() != null ? JobId.fromPb(results.getJobReference()) : null) @@ -1448,7 +1451,8 @@ public TableResult query(QueryJobConfiguration configuration, JobId jobId, JobOp // If all parameters passed in configuration are supported by the query() method on the backend, // put on fast path - QueryRequestInfo requestInfo = new QueryRequestInfo(configuration); + QueryRequestInfo requestInfo = + new QueryRequestInfo(configuration, getOptions().getUseInt64Timestamps()); if (requestInfo.isFastQuerySupported(jobId)) { // Be careful when setting the projectID in JobId, if a projectID is specified in the JobId, // the job created by the query method will use that project. This may cause the query to diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryOptions.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryOptions.java index e53439f023..465cc83058 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryOptions.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryOptions.java @@ -34,10 +34,11 @@ public class BigQueryOptions extends ServiceOptions { private static final int DEFAULT_READ_API_TIME_OUT = 60000; private static final String BIGQUERY_SCOPE = "https://www.googleapis.com/auth/bigquery"; private static final Set SCOPES = ImmutableSet.of(BIGQUERY_SCOPE); - private static final long serialVersionUID = -2437598817433266049L; + private static final long serialVersionUID = -2437598817433266048L; private final String location; // set the option ThrowNotFound when you want to throw the exception when the value not found private boolean setThrowNotFound; + private boolean useInt64Timestamps; private String queryPreviewEnabled = System.getenv("QUERY_PREVIEW_ENABLED"); public static class DefaultBigQueryFactory implements BigQueryFactory { @@ -63,6 +64,7 @@ public ServiceRpc create(BigQueryOptions options) { public static class Builder extends ServiceOptions.Builder { private String location; + private boolean useInt64Timestamps; private Builder() {} @@ -84,6 +86,11 @@ public Builder setLocation(String location) { return this; } + public Builder setUseInt64Timestamps(boolean useInt64Timestamps) { + this.useInt64Timestamps = useInt64Timestamps; + return this; + } + @Override public BigQueryOptions build() { return new BigQueryOptions(this); @@ -93,6 +100,7 @@ public BigQueryOptions build() { private BigQueryOptions(Builder builder) { super(BigQueryFactory.class, BigQueryRpcFactory.class, builder, new BigQueryDefaults()); this.location = builder.location; + this.useInt64Timestamps = builder.useInt64Timestamps; } private static class BigQueryDefaults implements ServiceDefaults { @@ -140,6 +148,10 @@ public void setThrowNotFound(boolean setThrowNotFound) { this.setThrowNotFound = setThrowNotFound; } + public void setUseInt64Timestamps(boolean useInt64Timestamps) { + this.useInt64Timestamps = useInt64Timestamps; + } + @VisibleForTesting public void setQueryPreviewEnabled(String queryPreviewEnabled) { this.queryPreviewEnabled = queryPreviewEnabled; @@ -149,6 +161,10 @@ public boolean getThrowNotFound() { return setThrowNotFound; } + public boolean getUseInt64Timestamps() { + return useInt64Timestamps; + } + @SuppressWarnings("unchecked") @Override public Builder toBuilder() { diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryAlgorithm.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryAlgorithm.java index 0429b7f005..7b168c12d1 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryAlgorithm.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryAlgorithm.java @@ -27,13 +27,13 @@ import com.google.api.gax.retrying.TimedRetryAlgorithmWithContext; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import java.time.Duration; import java.util.Iterator; import java.util.UUID; import java.util.concurrent.CancellationException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; -import org.threeten.bp.Duration; public class BigQueryRetryAlgorithm extends RetryAlgorithm { private final BigQueryRetryConfig bigQueryRetryConfig; @@ -67,7 +67,7 @@ public boolean shouldRetry( // Log retry info int attemptCount = nextAttemptSettings == null ? 0 : nextAttemptSettings.getAttemptCount(); Duration retryDelay = - nextAttemptSettings == null ? Duration.ZERO : nextAttemptSettings.getRetryDelay(); + nextAttemptSettings == null ? Duration.ZERO : nextAttemptSettings.getRetryDelayDuration(); String errorMessage = previousThrowable != null ? previousThrowable.getMessage() : ""; // Implementing shouldRetryBasedOnBigQueryRetryConfig so that we can retry exceptions based on diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Dataset.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Dataset.java index daadb9a857..4fc8577853 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Dataset.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Dataset.java @@ -164,6 +164,12 @@ public Builder setStorageBillingModel(String storageBillingModel) { return this; } + @Override + public Builder setMaxTimeTravelHours(Long maxTimeTravelHours) { + infoBuilder.setMaxTimeTravelHours(maxTimeTravelHours); + return this; + } + @Override public Dataset build() { return new Dataset(bigquery, infoBuilder); diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/DatasetInfo.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/DatasetInfo.java index b5fabe97d1..f9b7f03e17 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/DatasetInfo.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/DatasetInfo.java @@ -75,6 +75,7 @@ public Dataset apply(DatasetInfo datasetInfo) { private final String defaultCollation; private final ExternalDatasetReference externalDatasetReference; private final String storageBillingModel; + private final Long maxTimeTravelHours; /** A builder for {@code DatasetInfo} objects. */ public abstract static class Builder { @@ -142,6 +143,12 @@ public abstract Builder setExternalDatasetReference( */ public abstract Builder setStorageBillingModel(String storageBillingModel); + /** + * Optional. Defines the time travel window in hours. The value can be from 48 to 168 hours (2 + * to 7 days). The default value is 168 hours if this is not set. The value may be {@code null}. + */ + public abstract Builder setMaxTimeTravelHours(Long maxTimeTravelHours); + /** * The default encryption key for all tables in the dataset. Once this property is set, all * newly-created partitioned tables in the dataset will have encryption key set to this value, @@ -200,6 +207,7 @@ static final class BuilderImpl extends Builder { private String defaultCollation; private ExternalDatasetReference externalDatasetReference; private String storageBillingModel; + private Long maxTimeTravelHours; BuilderImpl() {} @@ -221,6 +229,7 @@ static final class BuilderImpl extends Builder { this.defaultCollation = datasetInfo.defaultCollation; this.externalDatasetReference = datasetInfo.externalDatasetReference; this.storageBillingModel = datasetInfo.storageBillingModel; + this.maxTimeTravelHours = datasetInfo.maxTimeTravelHours; } BuilderImpl(com.google.api.services.bigquery.model.Dataset datasetPb) { @@ -260,6 +269,7 @@ public Acl apply(Dataset.Access accessPb) { ExternalDatasetReference.fromPb(datasetPb.getExternalDatasetReference()); } this.storageBillingModel = datasetPb.getStorageBillingModel(); + this.maxTimeTravelHours = datasetPb.getMaxTimeTravelHours(); } @Override @@ -372,6 +382,12 @@ public Builder setStorageBillingModel(String storageBillingModel) { return this; } + @Override + public Builder setMaxTimeTravelHours(Long maxTimeTravelHours) { + this.maxTimeTravelHours = maxTimeTravelHours; + return this; + } + @Override public DatasetInfo build() { return new DatasetInfo(this); @@ -396,6 +412,7 @@ public DatasetInfo build() { defaultCollation = builder.defaultCollation; externalDatasetReference = builder.externalDatasetReference; storageBillingModel = builder.storageBillingModel; + maxTimeTravelHours = builder.maxTimeTravelHours; } /** Returns the dataset identity. */ @@ -529,6 +546,14 @@ public String getStorageBillingModel() { return storageBillingModel; } + /** + * Returns the number of hours that deleted or updated data will be available to be queried for + * all tables in the dataset. + */ + public Long getMaxTimeTravelHours() { + return maxTimeTravelHours; + } + /** * Returns information about the external metadata storage where the dataset is defined. Filled * out when the dataset type is EXTERNAL. @@ -562,6 +587,7 @@ public String toString() { .add("defaultCollation", defaultCollation) .add("externalDatasetReference", externalDatasetReference) .add("storageBillingModel", storageBillingModel) + .add("maxTimeTravelHours", maxTimeTravelHours) .toString(); } @@ -646,6 +672,9 @@ public Dataset.Access apply(Acl acl) { if (storageBillingModel != null) { datasetPb.setStorageBillingModel(storageBillingModel); } + if (maxTimeTravelHours != null) { + datasetPb.setMaxTimeTravelHours(maxTimeTravelHours); + } return datasetPb; } diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java index c5a8fab07c..d11df4b956 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java @@ -26,6 +26,7 @@ import com.google.common.io.BaseEncoding; import java.io.Serializable; import java.math.BigDecimal; +import java.math.BigInteger; import java.math.RoundingMode; import java.time.Duration; import java.time.Instant; @@ -46,10 +47,11 @@ public class FieldValue implements Serializable { private static final int MICROSECONDS = 1000000; - private static final long serialVersionUID = 469098630191710061L; + private static final long serialVersionUID = 469098630191710062L; private final Attribute attribute; private final Object value; + private final Boolean useInt64Timestamps; /** The field value's attribute, giving information on the field's content type. */ public enum Attribute { @@ -74,8 +76,13 @@ public enum Attribute { } private FieldValue(Attribute attribute, Object value) { + this(attribute, value, false); + } + + private FieldValue(Attribute attribute, Object value, Boolean useInt64Timestamps) { this.attribute = checkNotNull(attribute); this.value = value; + this.useInt64Timestamps = useInt64Timestamps; } /** @@ -107,6 +114,10 @@ public Object getValue() { return value; } + public Boolean getUseInt64Timestamps() { + return useInt64Timestamps; + } + /** * Returns this field's value as a {@link String}. This method should only be used if the * corresponding field has primitive type ({@link LegacySQLTypeName#BYTES}, {@link @@ -207,6 +218,9 @@ public boolean getBooleanValue() { */ @SuppressWarnings("unchecked") public long getTimestampValue() { + if (useInt64Timestamps) { + return new BigInteger(getStringValue()).longValue(); + } // timestamps are encoded in the format 1408452095.22 where the integer part is seconds since // epoch (e.g. 1408452095.22 == 2014-08-19 07:41:35.220 -05:00) BigDecimal secondsWithMicro = new BigDecimal(getStringValue()); @@ -317,12 +331,13 @@ public String toString() { return MoreObjects.toStringHelper(this) .add("attribute", attribute) .add("value", value) + .add("useInt64Timestamps", useInt64Timestamps) .toString(); } @Override public final int hashCode() { - return Objects.hash(attribute, value); + return Objects.hash(attribute, value, useInt64Timestamps); } @Override @@ -334,7 +349,9 @@ public final boolean equals(Object obj) { return false; } FieldValue other = (FieldValue) obj; - return attribute == other.attribute && Objects.equals(value, other.value); + return attribute == other.attribute + && Objects.equals(value, other.value) + && Objects.equals(useInt64Timestamps, other.useInt64Timestamps); } /** @@ -353,29 +370,38 @@ public final boolean equals(Object obj) { */ @BetaApi public static FieldValue of(Attribute attribute, Object value) { - return new FieldValue(attribute, value); + return of(attribute, value, false); + } + + @BetaApi + public static FieldValue of(Attribute attribute, Object value, Boolean useInt64Timestamps) { + return new FieldValue(attribute, value, useInt64Timestamps); } static FieldValue fromPb(Object cellPb) { - return fromPb(cellPb, null); + return fromPb(cellPb, null, false); } @SuppressWarnings("unchecked") - static FieldValue fromPb(Object cellPb, Field recordSchema) { + static FieldValue fromPb(Object cellPb, Field recordSchema, Boolean useInt64Timestamps) { if (Data.isNull(cellPb)) { - return FieldValue.of(Attribute.PRIMITIVE, null); + return FieldValue.of(Attribute.PRIMITIVE, null, useInt64Timestamps); } if (cellPb instanceof String) { if ((recordSchema != null) && (recordSchema.getType() == LegacySQLTypeName.RANGE) && (recordSchema.getRangeElementType() != null)) { return FieldValue.of( - Attribute.RANGE, Range.of((String) cellPb, recordSchema.getRangeElementType())); + Attribute.RANGE, + Range.of((String) cellPb, recordSchema.getRangeElementType()), + useInt64Timestamps); } - return FieldValue.of(Attribute.PRIMITIVE, cellPb); + return FieldValue.of(Attribute.PRIMITIVE, cellPb, useInt64Timestamps); } if (cellPb instanceof List) { - return FieldValue.of(Attribute.REPEATED, FieldValueList.fromPb((List) cellPb, null)); + return FieldValue.of( + Attribute.REPEATED, + FieldValueList.fromPb((List) cellPb, null, useInt64Timestamps)); } if (cellPb instanceof Map) { Map cellMapPb = (Map) cellPb; @@ -383,12 +409,13 @@ static FieldValue fromPb(Object cellPb, Field recordSchema) { FieldList subFieldsSchema = recordSchema != null ? recordSchema.getSubFields() : null; return FieldValue.of( Attribute.RECORD, - FieldValueList.fromPb((List) cellMapPb.get("f"), subFieldsSchema)); + FieldValueList.fromPb( + (List) cellMapPb.get("f"), subFieldsSchema, useInt64Timestamps)); } // This should never be the case when we are processing a first level table field (i.e. a // row's field, not a record sub-field) if (cellMapPb.containsKey("v")) { - return FieldValue.fromPb(cellMapPb.get("v"), recordSchema); + return FieldValue.fromPb(cellMapPb.get("v"), recordSchema, useInt64Timestamps); } } throw new IllegalArgumentException("Unexpected table cell format"); diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValueList.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValueList.java index 5035bb164a..18d2155a58 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValueList.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValueList.java @@ -112,6 +112,10 @@ FieldValueList withSchema(FieldList schema) { } static FieldValueList fromPb(List rowPb, FieldList schema) { + return fromPb(rowPb, schema, false); + } + + static FieldValueList fromPb(List rowPb, FieldList schema, Boolean useInt64Timestamps) { List row = new ArrayList<>(rowPb.size()); if (schema != null) { if (schema.size() != rowPb.size()) { @@ -120,11 +124,11 @@ static FieldValueList fromPb(List rowPb, FieldList schema) { Iterator schemaIter = schema.iterator(); Iterator rowPbIter = rowPb.iterator(); while (rowPbIter.hasNext() && schemaIter.hasNext()) { - row.add(FieldValue.fromPb(rowPbIter.next(), schemaIter.next())); + row.add(FieldValue.fromPb(rowPbIter.next(), schemaIter.next(), useInt64Timestamps)); } } else { for (Object cellPb : rowPb) { - row.add(FieldValue.fromPb(cellPb, null)); + row.add(FieldValue.fromPb(cellPb, null, useInt64Timestamps)); } } diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java index 793b25687b..00edefb9fb 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java @@ -31,13 +31,13 @@ import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.ObjectInputStream; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; -import org.threeten.bp.Duration; /** * A Google BigQuery Job. @@ -52,20 +52,20 @@ public class Job extends JobInfo { private static final RetrySettings DEFAULT_JOB_WAIT_SETTINGS = RetrySettings.newBuilder() - .setTotalTimeout(Duration.ofHours(12L)) - .setInitialRetryDelay(Duration.ofSeconds(1L)) + .setTotalTimeoutDuration(Duration.ofHours(12L)) + .setInitialRetryDelayDuration(Duration.ofSeconds(1L)) .setRetryDelayMultiplier(2.0) .setJittered(true) - .setMaxRetryDelay(Duration.ofMinutes(1L)) + .setMaxRetryDelayDuration(Duration.ofMinutes(1L)) .build(); static final RetrySettings DEFAULT_QUERY_JOB_WAIT_SETTINGS = RetrySettings.newBuilder() - .setTotalTimeout(Duration.ofHours(12L)) - .setInitialRetryDelay(Duration.ofSeconds(3L)) + .setTotalTimeoutDuration(Duration.ofHours(12L)) + .setInitialRetryDelayDuration(Duration.ofSeconds(3L)) .setRetryDelayMultiplier(1.0) .setJittered(true) - .setMaxRetryDelay(Duration.ofSeconds(3L)) + .setMaxRetryDelayDuration(Duration.ofSeconds(3L)) .build(); static final QueryResultsOption[] DEFAULT_QUERY_WAIT_OPTIONS = { diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LoadConfiguration.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LoadConfiguration.java index d4ed810449..58cf986706 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LoadConfiguration.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LoadConfiguration.java @@ -16,6 +16,7 @@ package com.google.cloud.bigquery; +import com.google.api.core.InternalApi; import com.google.cloud.bigquery.JobInfo.CreateDisposition; import com.google.cloud.bigquery.JobInfo.SchemaUpdateOption; import com.google.cloud.bigquery.JobInfo.WriteDisposition; @@ -99,17 +100,17 @@ interface Builder { Builder setIgnoreUnknownValues(Boolean ignoreUnknownValues); /** - * [Experimental] Sets options allowing the schema of the destination table to be updated as a - * side effect of the load job. Schema update options are supported in two cases: when - * writeDisposition is WRITE_APPEND; when writeDisposition is WRITE_TRUNCATE and the destination - * table is a partition of a table, specified by partition decorators. For normal tables, - * WRITE_TRUNCATE will always overwrite the schema. + * Sets options allowing the schema of the destination table to be updated as a side effect of + * the load job. Schema update options are supported in two cases: when writeDisposition is + * WRITE_APPEND; when writeDisposition is WRITE_TRUNCATE and the destination table is a + * partition of a table, specified by partition decorators. For normal tables, WRITE_TRUNCATE + * will always overwrite the schema. */ + @InternalApi Builder setSchemaUpdateOptions(List schemaUpdateOptions); - /** - * [Experimental] Sets automatic inference of the options and schema for CSV and JSON sources. - */ + /** Sets automatic inference of the options and schema for CSV and JSON sources. */ + @InternalApi Builder setAutodetect(Boolean autodetect); /** Sets the time partitioning specification for the destination table. */ @@ -202,18 +203,19 @@ interface Builder { DatastoreBackupOptions getDatastoreBackupOptions(); /** - * [Experimental] Returns options allowing the schema of the destination table to be updated as a - * side effect of the load job. Schema update options are supported in two cases: when - * writeDisposition is WRITE_APPEND; when writeDisposition is WRITE_TRUNCATE and the destination - * table is a partition of a table, specified by partition decorators. For normal tables, - * WRITE_TRUNCATE will always overwrite the schema. + * Returns options allowing the schema of the destination table to be updated as a side effect of + * the load job. Schema update options are supported in two cases: when writeDisposition is + * WRITE_APPEND; when writeDisposition is WRITE_TRUNCATE and the destination table is a partition + * of a table, specified by partition decorators. For normal tables, WRITE_TRUNCATE will always + * overwrite the schema. */ + @InternalApi List getSchemaUpdateOptions(); /** - * [Experimental] Returns whether automatic inference of the options and schema for CSV and JSON - * sources is set. + * Returns whether automatic inference of the options and schema for CSV and JSON sources is set. */ + @InternalApi Boolean getAutodetect(); /** Returns the time partitioning specification defined for the destination table. */ diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryParameterValue.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryParameterValue.java index a7469a3a68..0487c3f7c3 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryParameterValue.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryParameterValue.java @@ -16,11 +16,12 @@ package com.google.cloud.bigquery; -import static org.threeten.bp.temporal.ChronoField.HOUR_OF_DAY; -import static org.threeten.bp.temporal.ChronoField.MINUTE_OF_HOUR; -import static org.threeten.bp.temporal.ChronoField.NANO_OF_SECOND; -import static org.threeten.bp.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import com.google.api.core.ObsoleteApi; import com.google.api.services.bigquery.model.QueryParameterType; import com.google.api.services.bigquery.model.RangeValue; import com.google.auto.value.AutoValue; @@ -33,17 +34,17 @@ import com.google.gson.JsonObject; import java.io.Serializable; import java.math.BigDecimal; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Nullable; -import org.threeten.bp.Instant; -import org.threeten.bp.ZoneOffset; -import org.threeten.bp.format.DateTimeFormatter; -import org.threeten.bp.format.DateTimeFormatterBuilder; -import org.threeten.bp.format.DateTimeParseException; import org.threeten.extra.PeriodDuration; /** @@ -299,7 +300,12 @@ public static QueryParameterValue bytes(byte[] value) { return of(value, StandardSQLTypeName.BYTES); } - /** Creates a {@code QueryParameterValue} object with a type of TIMESTAMP. */ + /** + * Creates a {@code QueryParameterValue} object with a type of TIMESTAMP. + * + * @param value Microseconds since epoch, e.g. 1733945416000000 corresponds to 2024-12-11 + * 19:30:16.929Z + */ public static QueryParameterValue timestamp(Long value) { return of(value, StandardSQLTypeName.TIMESTAMP); } @@ -345,7 +351,11 @@ public static QueryParameterValue interval(String value) { return of(value, StandardSQLTypeName.INTERVAL); } - /** Creates a {@code QueryParameterValue} object with a type of INTERVAL. */ + /** + * Creates a {@code QueryParameterValue} object with a type of INTERVAL. This method is obsolete. + * Use {@link #interval(String)} instead. + */ + @ObsoleteApi("Use interval(String) instead") public static QueryParameterValue interval(PeriodDuration value) { return of(value, StandardSQLTypeName.INTERVAL); } diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryRequestInfo.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryRequestInfo.java index 00a11f723b..1a56872a15 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryRequestInfo.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryRequestInfo.java @@ -16,6 +16,7 @@ package com.google.cloud.bigquery; +import com.google.api.services.bigquery.model.DataFormatOptions; import com.google.api.services.bigquery.model.QueryParameter; import com.google.api.services.bigquery.model.QueryRequest; import com.google.cloud.bigquery.QueryJobConfiguration.JobCreationMode; @@ -42,8 +43,9 @@ final class QueryRequestInfo { private final Boolean useQueryCache; private final Boolean useLegacySql; private final JobCreationMode jobCreationMode; + private final DataFormatOptions formatOptions; - QueryRequestInfo(QueryJobConfiguration config) { + QueryRequestInfo(QueryJobConfiguration config, Boolean useInt64Timestamps) { this.config = config; this.connectionProperties = config.getConnectionProperties(); this.defaultDataset = config.getDefaultDataset(); @@ -58,6 +60,7 @@ final class QueryRequestInfo { this.useLegacySql = config.useLegacySql(); this.useQueryCache = config.useQueryCache(); this.jobCreationMode = config.getJobCreationMode(); + this.formatOptions = new DataFormatOptions().setUseInt64Timestamp(useInt64Timestamps); } boolean isFastQuerySupported(JobId jobId) { @@ -122,6 +125,9 @@ QueryRequest toPb() { if (jobCreationMode != null) { request.setJobCreationMode(jobCreationMode.toString()); } + if (formatOptions != null) { + request.setFormatOptions(formatOptions); + } return request; } @@ -141,6 +147,7 @@ public String toString() { .add("useQueryCache", useQueryCache) .add("useLegacySql", useLegacySql) .add("jobCreationMode", jobCreationMode) + .add("formatOptions", formatOptions.getUseInt64Timestamp()) .toString(); } @@ -159,7 +166,8 @@ public int hashCode() { createSession, useQueryCache, useLegacySql, - jobCreationMode); + jobCreationMode, + formatOptions); } @Override diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelper.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelper.java index 784ca984fa..2853c46761 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelper.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelper.java @@ -24,10 +24,10 @@ import com.google.cloud.http.HttpTransportOptions; import java.io.IOException; import java.io.InputStream; +import java.time.Duration; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; -import org.threeten.bp.Duration; /** * Utility to create a remote BigQuery configuration for testing. BigQuery options can be obtained @@ -144,13 +144,13 @@ private static RetrySettings retrySettings() { long totalTimeOut = 120000L; return RetrySettings.newBuilder() .setMaxAttempts(maxAttempts) - .setMaxRetryDelay(Duration.ofMillis(maxRetryDelay)) - .setTotalTimeout(Duration.ofMillis(totalTimeOut)) - .setInitialRetryDelay(Duration.ofMillis(initialRetryDelay)) + .setMaxRetryDelayDuration(Duration.ofMillis(maxRetryDelay)) + .setTotalTimeoutDuration(Duration.ofMillis(totalTimeOut)) + .setInitialRetryDelayDuration(Duration.ofMillis(initialRetryDelay)) .setRetryDelayMultiplier(retryDelayMultiplier) - .setInitialRpcTimeout(Duration.ofMillis(totalTimeOut)) + .setInitialRpcTimeoutDuration(Duration.ofMillis(totalTimeOut)) .setRpcTimeoutMultiplier(retryDelayMultiplier) - .setMaxRpcTimeout(Duration.ofMillis(totalTimeOut)) + .setMaxRpcTimeoutDuration(Duration.ofMillis(totalTimeOut)) .build(); } diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/DatasetInfoTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/DatasetInfoTest.java index df62b7dac8..49a392baf7 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/DatasetInfoTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/DatasetInfoTest.java @@ -17,6 +17,7 @@ package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -59,6 +60,8 @@ public class DatasetInfoTest { private static final EncryptionConfiguration DATASET_ENCRYPTION_CONFIGURATION = EncryptionConfiguration.newBuilder().setKmsKeyName("KMS_KEY_1").build(); private static final String STORAGE_BILLING_MODEL = "LOGICAL"; + private static final Long MAX_TIME_TRAVEL_HOURS_5_DAYS = 120L; + private static final Long MAX_TIME_TRAVEL_HOURS_7_DAYS = 168L; private static final ExternalDatasetReference EXTERNAL_DATASET_REFERENCE = ExternalDatasetReference.newBuilder() @@ -81,6 +84,7 @@ public class DatasetInfoTest { .setDefaultEncryptionConfiguration(DATASET_ENCRYPTION_CONFIGURATION) .setDefaultPartitionExpirationMs(DEFAULT_PARTITION__EXPIRATION) .setStorageBillingModel(STORAGE_BILLING_MODEL) + .setMaxTimeTravelHours(MAX_TIME_TRAVEL_HOURS_7_DAYS) .build(); private static final DatasetInfo DATASET_INFO_COMPLETE = DATASET_INFO @@ -92,6 +96,8 @@ public class DatasetInfoTest { DATASET_INFO.toBuilder().setAcl(ACCESS_RULES_IAM_MEMBER).build(); private static final DatasetInfo DATASET_INFO_COMPLETE_WITH_EXTERNAL_DATASET_REFERENCE = DATASET_INFO.toBuilder().setExternalDatasetReference(EXTERNAL_DATASET_REFERENCE).build(); + private static final DatasetInfo DATASET_INFO_WITH_MAX_TIME_TRAVEL_5_DAYS = + DATASET_INFO.toBuilder().setMaxTimeTravelHours(MAX_TIME_TRAVEL_HOURS_5_DAYS).build(); @Test public void testToBuilder() { @@ -173,6 +179,10 @@ public void testBuilder() { EXTERNAL_DATASET_REFERENCE, DATASET_INFO_COMPLETE_WITH_EXTERNAL_DATASET_REFERENCE.getExternalDatasetReference()); assertEquals(STORAGE_BILLING_MODEL, DATASET_INFO_COMPLETE.getStorageBillingModel()); + assertEquals(MAX_TIME_TRAVEL_HOURS_7_DAYS, DATASET_INFO.getMaxTimeTravelHours()); + assertEquals( + MAX_TIME_TRAVEL_HOURS_5_DAYS, + DATASET_INFO_WITH_MAX_TIME_TRAVEL_5_DAYS.getMaxTimeTravelHours()); } @Test @@ -194,6 +204,7 @@ public void testOf() { assertTrue(datasetInfo.getLabels().isEmpty()); assertNull(datasetInfo.getExternalDatasetReference()); assertNull(datasetInfo.getStorageBillingModel()); + assertNull(datasetInfo.getMaxTimeTravelHours()); datasetInfo = DatasetInfo.of(DATASET_ID); assertEquals(DATASET_ID, datasetInfo.getDatasetId()); @@ -212,6 +223,7 @@ public void testOf() { assertTrue(datasetInfo.getLabels().isEmpty()); assertNull(datasetInfo.getExternalDatasetReference()); assertNull(datasetInfo.getStorageBillingModel()); + assertNull(datasetInfo.getMaxTimeTravelHours()); } @Test @@ -229,6 +241,16 @@ public void testSetProjectId() { assertEquals(DATASET_INFO_COMPLETE, DATASET_INFO.setProjectId("project")); } + @Test + public void testSetMaxTimeTravelHours() { + assertNotEquals( + DATASET_INFO_WITH_MAX_TIME_TRAVEL_5_DAYS.getMaxTimeTravelHours(), + DATASET_INFO.getMaxTimeTravelHours()); + assertEquals( + DATASET_INFO_WITH_MAX_TIME_TRAVEL_5_DAYS, + DATASET_INFO.toBuilder().setMaxTimeTravelHours(MAX_TIME_TRAVEL_HOURS_5_DAYS).build()); + } + private void compareDatasets(DatasetInfo expected, DatasetInfo value) { assertEquals(expected, value); assertEquals(expected.getDatasetId(), value.getDatasetId()); @@ -249,5 +271,6 @@ private void compareDatasets(DatasetInfo expected, DatasetInfo value) { expected.getDefaultPartitionExpirationMs(), value.getDefaultPartitionExpirationMs()); assertEquals(expected.getExternalDatasetReference(), value.getExternalDatasetReference()); assertEquals(expected.getStorageBillingModel(), value.getStorageBillingModel()); + assertEquals(expected.getMaxTimeTravelHours(), value.getMaxTimeTravelHours()); } } diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/DatasetTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/DatasetTest.java index bc42976b23..e2d7c635ce 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/DatasetTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/DatasetTest.java @@ -67,6 +67,7 @@ public class DatasetTest { private static final DatasetInfo DATASET_INFO = DatasetInfo.newBuilder(DATASET_ID).build(); private static final Field FIELD = Field.of("FieldName", LegacySQLTypeName.INTEGER); private static final String STORAGE_BILLING_MODEL = "LOGICAL"; + private static final Long MAX_TIME_TRAVEL_HOURS = 168L; private static final StandardTableDefinition TABLE_DEFINITION = StandardTableDefinition.of(Schema.of(FIELD)); private static final ViewDefinition VIEW_DEFINITION = ViewDefinition.of("QUERY"); @@ -122,6 +123,7 @@ public void testBuilder() { .setSelfLink(SELF_LINK) .setLabels(LABELS) .setStorageBillingModel(STORAGE_BILLING_MODEL) + .setMaxTimeTravelHours(MAX_TIME_TRAVEL_HOURS) .build(); assertEquals(DATASET_ID, builtDataset.getDatasetId()); assertEquals(ACCESS_RULES, builtDataset.getAcl()); @@ -136,6 +138,7 @@ public void testBuilder() { assertEquals(SELF_LINK, builtDataset.getSelfLink()); assertEquals(LABELS, builtDataset.getLabels()); assertEquals(STORAGE_BILLING_MODEL, builtDataset.getStorageBillingModel()); + assertEquals(MAX_TIME_TRAVEL_HOURS, builtDataset.getMaxTimeTravelHours()); } @Test @@ -344,6 +347,7 @@ public void testExternalDatasetReference() { .setLabels(LABELS) .setExternalDatasetReference(EXTERNAL_DATASET_REFERENCE) .setStorageBillingModel(STORAGE_BILLING_MODEL) + .setMaxTimeTravelHours(MAX_TIME_TRAVEL_HOURS) .build(); assertEquals( EXTERNAL_DATASET_REFERENCE, @@ -374,5 +378,6 @@ private void compareDatasetInfo(DatasetInfo expected, DatasetInfo value) { assertEquals(expected.getLastModified(), value.getLastModified()); assertEquals(expected.getExternalDatasetReference(), value.getExternalDatasetReference()); assertEquals(expected.getStorageBillingModel(), value.getStorageBillingModel()); + assertEquals(expected.getMaxTimeTravelHours(), value.getMaxTimeTravelHours()); } } diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueListTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueListTest.java index 7d10a97504..5ade7c2298 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueListTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueListTest.java @@ -17,6 +17,7 @@ package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import com.google.api.client.util.Data; @@ -52,6 +53,12 @@ public class FieldValueListTest { Field.of("tenth", LegacySQLTypeName.NUMERIC), Field.of("eleventh", LegacySQLTypeName.BIGNUMERIC)); + private final FieldList schemaLosslessTimestamp = + FieldList.of( + Field.of("first", LegacySQLTypeName.BOOLEAN), + Field.of("second", LegacySQLTypeName.INTEGER), + Field.of("third", LegacySQLTypeName.TIMESTAMP)); + private final Map integerPb = ImmutableMap.of("v", "1"); private final Map floatPb = ImmutableMap.of("v", "1.5"); private final Map stringPb = ImmutableMap.of("v", "string"); @@ -68,10 +75,15 @@ public class FieldValueListTest { "v", "99999999999999999999999999999999999999.99999999999999999999999999999999999999"); private final FieldValue booleanFv = FieldValue.of(Attribute.PRIMITIVE, "false"); + private final FieldValue booleanLosslessTimestampFv = + FieldValue.of(Attribute.PRIMITIVE, "false", true); private final FieldValue integerFv = FieldValue.of(Attribute.PRIMITIVE, "1"); + private final FieldValue integerLosslessTimestampFv = + FieldValue.of(Attribute.PRIMITIVE, "1", true); private final FieldValue floatFv = FieldValue.of(Attribute.PRIMITIVE, "1.5"); private final FieldValue stringFv = FieldValue.of(Attribute.PRIMITIVE, "string"); private final FieldValue timestampFv = FieldValue.of(Attribute.PRIMITIVE, "42"); + private final FieldValue losslessTimestampFv = FieldValue.of(Attribute.PRIMITIVE, "42", true); private final FieldValue bytesFv = FieldValue.of(Attribute.PRIMITIVE, BYTES_BASE64); private final FieldValue nullFv = FieldValue.of(Attribute.PRIMITIVE, null); private final FieldValue repeatedFv = @@ -117,11 +129,25 @@ public class FieldValueListTest { bigNumericFv), schema); + private final List fieldValuesLosslessTimestampPb = + ImmutableList.of(booleanPb, integerPb, timestampPb); + private final FieldValueList fieldValuesLosslessTimestamp = + FieldValueList.of( + ImmutableList.of( + booleanLosslessTimestampFv, integerLosslessTimestampFv, losslessTimestampFv), + schemaLosslessTimestamp); + @Test public void testFromPb() { assertEquals(fieldValues, FieldValueList.fromPb(fieldValuesPb, schema)); // Schema does not influence values equality assertEquals(fieldValues, FieldValueList.fromPb(fieldValuesPb, null)); + + assertNotEquals(fieldValues, FieldValueList.fromPb(fieldValuesPb, null, true)); + + assertEquals( + fieldValuesLosslessTimestamp, + FieldValueList.fromPb(fieldValuesLosslessTimestampPb, null, true)); } @Test diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueTest.java index 6dbe817078..4ec527f7ce 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueTest.java @@ -132,6 +132,25 @@ public void testTimestamp() { assertEquals(expected, received); } + @Test + public void testInt64Timestamp() { + FieldValue lossyFieldValue = + FieldValue.of(FieldValue.Attribute.PRIMITIVE, "1.9954383398377106E10"); + long lossy = lossyFieldValue.getTimestampValue(); + + FieldValue losslessFieldValue = + FieldValue.of(FieldValue.Attribute.PRIMITIVE, "19954383398377106", true); + long lossless = losslessFieldValue.getTimestampValue(); + + assertEquals(lossy, lossless); + + FieldValue fieldValue = + FieldValue.of(FieldValue.Attribute.PRIMITIVE, "19954383398377106", true); + long received = fieldValue.getTimestampValue(); + long expected = 19954383398377106L; + assertEquals(expected, received); + } + @Test public void testEquals() { FieldValue booleanValue = FieldValue.of(FieldValue.Attribute.PRIMITIVE, "false"); diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java index 396bb754ac..f12d9fcafa 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java @@ -41,6 +41,7 @@ import com.google.cloud.bigquery.JobStatistics.QueryStatistics; import com.google.cloud.bigquery.JobStatus.State; import com.google.common.collect.ImmutableList; +import java.time.Duration; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -48,7 +49,6 @@ import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoRule; -import org.threeten.bp.Duration; @RunWith(MockitoJUnitRunner.class) public class JobTest { @@ -83,8 +83,8 @@ public class JobTest { private static final RetryOption[] TEST_RETRY_OPTIONS = new RetryOption[] { - RetryOption.totalTimeout(Duration.ofSeconds(3)), - RetryOption.initialRetryDelay(Duration.ofMillis(1L)), + RetryOption.totalTimeoutDuration(Duration.ofSeconds(3)), + RetryOption.initialRetryDelayDuration(Duration.ofMillis(1L)), RetryOption.jittered(false), RetryOption.retryDelayMultiplier(1.0) }; @@ -402,7 +402,8 @@ public void testWaitForWithTimeout() throws InterruptedException { when(bigquery.getJob(JOB_INFO.getJobId(), expectedOptions)).thenReturn(runningJob); when(bigquery.getJob(JOB_INFO.getJobId(), expectedOptions)).thenReturn(runningJob); try { - job.waitFor(concat(TEST_RETRY_OPTIONS, RetryOption.totalTimeout(Duration.ofMillis(3)))); + job.waitFor( + concat(TEST_RETRY_OPTIONS, RetryOption.totalTimeoutDuration(Duration.ofMillis(3)))); Assert.fail(); } catch (BigQueryException expected) { Assert.assertNotNull(expected.getMessage()); diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryParameterValueTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryParameterValueTest.java index e0ea9d68dd..75060a4f04 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryParameterValueTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryParameterValueTest.java @@ -17,17 +17,21 @@ package com.google.cloud.bigquery; import static com.google.common.truth.Truth.assertThat; -import static org.threeten.bp.temporal.ChronoField.HOUR_OF_DAY; -import static org.threeten.bp.temporal.ChronoField.MINUTE_OF_HOUR; -import static org.threeten.bp.temporal.ChronoField.NANO_OF_SECOND; -import static org.threeten.bp.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import com.google.api.services.bigquery.model.QueryParameterType; import com.google.common.collect.ImmutableMap; import com.google.gson.JsonObject; import java.math.BigDecimal; import java.text.ParseException; +import java.time.Instant; import java.time.Period; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -35,11 +39,6 @@ import java.util.Map; import org.junit.Assert; import org.junit.Test; -import org.threeten.bp.Instant; -import org.threeten.bp.ZoneOffset; -import org.threeten.bp.format.DateTimeFormatter; -import org.threeten.bp.format.DateTimeFormatterBuilder; -import org.threeten.bp.jdk8.Jdk8Methods; import org.threeten.extra.PeriodDuration; public class QueryParameterValueTest { @@ -338,8 +337,8 @@ public void testTimestampFromLong() { public void testTimestampWithFormatter() { long timestampInMicroseconds = 1571068536842L * 1000 + 123; long microseconds = 1_000_000; - long secs = Jdk8Methods.floorDiv(timestampInMicroseconds, microseconds); - int nano = (int) Jdk8Methods.floorMod(timestampInMicroseconds, microseconds) * 1000; + long secs = Math.floorDiv(timestampInMicroseconds, microseconds); + int nano = (int) Math.floorMod(timestampInMicroseconds, microseconds) * 1000; Instant instant = Instant.ofEpochSecond(secs, nano); String expected = TIMESTAMPFORMATTER.format(instant); assertThat(expected) diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryRequestInfoTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryRequestInfoTest.java index 0d9464c768..420c014846 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryRequestInfoTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryRequestInfoTest.java @@ -17,7 +17,9 @@ package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; import com.google.api.services.bigquery.model.QueryRequest; import com.google.cloud.bigquery.JobInfo.CreateDisposition; @@ -136,7 +138,7 @@ public class QueryRequestInfoTest { .setMaxResults(100L) .setJobCreationMode(jobCreationModeRequired) .build(); - QueryRequestInfo REQUEST_INFO = new QueryRequestInfo(QUERY_JOB_CONFIGURATION); + QueryRequestInfo REQUEST_INFO = new QueryRequestInfo(QUERY_JOB_CONFIGURATION, false); private static final QueryJobConfiguration QUERY_JOB_CONFIGURATION_SUPPORTED = QueryJobConfiguration.newBuilder(QUERY) .setUseQueryCache(USE_QUERY_CACHE) @@ -150,7 +152,8 @@ public class QueryRequestInfoTest { .setCreateSession(CREATE_SESSION) .setMaxResults(100L) .build(); - QueryRequestInfo REQUEST_INFO_SUPPORTED = new QueryRequestInfo(QUERY_JOB_CONFIGURATION_SUPPORTED); + QueryRequestInfo REQUEST_INFO_SUPPORTED = + new QueryRequestInfo(QUERY_JOB_CONFIGURATION_SUPPORTED, false); @Test public void testIsFastQuerySupported() { @@ -171,8 +174,19 @@ public void testToPb() { @Test public void equalTo() { compareQueryRequestInfo( - new QueryRequestInfo(QUERY_JOB_CONFIGURATION_SUPPORTED), REQUEST_INFO_SUPPORTED); - compareQueryRequestInfo(new QueryRequestInfo(QUERY_JOB_CONFIGURATION), REQUEST_INFO); + new QueryRequestInfo(QUERY_JOB_CONFIGURATION_SUPPORTED, false), REQUEST_INFO_SUPPORTED); + compareQueryRequestInfo(new QueryRequestInfo(QUERY_JOB_CONFIGURATION, false), REQUEST_INFO); + } + + @Test + public void testInt64Timestamp() { + QueryRequestInfo requestInfo = new QueryRequestInfo(QUERY_JOB_CONFIGURATION, false); + QueryRequest requestPb = requestInfo.toPb(); + assertFalse(requestPb.getFormatOptions().getUseInt64Timestamp()); + + QueryRequestInfo requestInfoLosslessTs = new QueryRequestInfo(QUERY_JOB_CONFIGURATION, true); + QueryRequest requestLosslessTsPb = requestInfoLosslessTs.toPb(); + assertTrue(requestLosslessTsPb.getFormatOptions().getUseInt64Timestamp()); } /* @@ -199,5 +213,6 @@ private void compareQueryRequestInfo(QueryRequestInfo expected, QueryRequestInfo assertEquals(expectedQueryReq.getUseQueryCache(), actualQueryReq.getUseQueryCache()); assertEquals(expectedQueryReq.getUseLegacySql(), actualQueryReq.getUseLegacySql()); assertEquals(expectedQueryReq.get("jobCreationMode"), actualQueryReq.get("jobCreationMode")); + assertEquals(expectedQueryReq.getFormatOptions(), actualQueryReq.getFormatOptions()); } } diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java index b1cb8d8451..86652c1648 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java @@ -169,6 +169,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; +import java.time.Duration; import java.time.Instant; import java.time.LocalTime; import java.time.Period; @@ -195,7 +196,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; -import org.threeten.bp.Duration; import org.threeten.extra.PeriodDuration; public class ITBigQueryTest { @@ -213,6 +213,8 @@ public class ITBigQueryTest { private static final String PROJECT_ID = ServiceOptions.getDefaultProjectId(); private static final String RANDOM_ID = UUID.randomUUID().toString().substring(0, 8); private static final String STORAGE_BILLING_MODEL = "LOGICAL"; + private static final Long MAX_TIME_TRAVEL_HOURS = 120L; + private static final Long MAX_TIME_TRAVEL_HOURS_DEFAULT = 168L; private static final String CLOUD_SAMPLES_DATA = Optional.fromNullable(System.getenv("CLOUD_SAMPLES_DATA_BUCKET")).or("cloud-samples-data"); private static final Map LABELS = @@ -1214,6 +1216,7 @@ public void testGetDatasetWithSelectedFields() { assertNull(dataset.getLocation()); assertNull(dataset.getSelfLink()); assertNull(dataset.getStorageBillingModel()); + assertNull(dataset.getMaxTimeTravelHours()); } @Test @@ -1230,6 +1233,7 @@ public void testUpdateDataset() { assertThat(dataset.getDescription()).isEqualTo("Some Description"); assertThat(dataset.getLabels()).containsExactly("a", "b"); assertThat(dataset.getStorageBillingModel()).isNull(); + assertThat(dataset.getMaxTimeTravelHours()).isNull(); Map updateLabels = new HashMap<>(); updateLabels.put("x", "y"); @@ -1241,10 +1245,12 @@ public void testUpdateDataset() { .setDescription("Updated Description") .setLabels(updateLabels) .setStorageBillingModel("LOGICAL") + .setMaxTimeTravelHours(MAX_TIME_TRAVEL_HOURS) .build()); assertThat(updatedDataset.getDescription()).isEqualTo("Updated Description"); assertThat(updatedDataset.getLabels()).containsExactly("x", "y"); assertThat(updatedDataset.getStorageBillingModel()).isEqualTo("LOGICAL"); + assertThat(updatedDataset.getMaxTimeTravelHours()).isEqualTo(MAX_TIME_TRAVEL_HOURS); updatedDataset = bigquery.update(updatedDataset.toBuilder().setLabels(null).build()); assertThat(updatedDataset.getLabels()).isEmpty(); @@ -1275,6 +1281,7 @@ public void testUpdateDatasetWithSelectedFields() { assertNull(updatedDataset.getLocation()); assertNull(updatedDataset.getSelfLink()); assertNull(updatedDataset.getStorageBillingModel()); + assertNull(updatedDataset.getMaxTimeTravelHours()); assertTrue(dataset.delete()); } @@ -1630,6 +1637,40 @@ public void testCreateDatasetWithSpecifiedStorageBillingModel() { RemoteBigQueryHelper.forceDelete(bigquery, billingModelDataset); } + @Test + public void testCreateDatasetWithSpecificMaxTimeTravelHours() { + String timeTravelDataset = RemoteBigQueryHelper.generateDatasetName(); + DatasetInfo info = + DatasetInfo.newBuilder(timeTravelDataset) + .setDescription(DESCRIPTION) + .setMaxTimeTravelHours(MAX_TIME_TRAVEL_HOURS) + .setLabels(LABELS) + .build(); + bigquery.create(info); + + Dataset dataset = bigquery.getDataset(DatasetId.of(timeTravelDataset)); + assertEquals(MAX_TIME_TRAVEL_HOURS, dataset.getMaxTimeTravelHours()); + + RemoteBigQueryHelper.forceDelete(bigquery, timeTravelDataset); + } + + @Test + public void testCreateDatasetWithDefaultMaxTimeTravelHours() { + String timeTravelDataset = RemoteBigQueryHelper.generateDatasetName(); + DatasetInfo info = + DatasetInfo.newBuilder(timeTravelDataset) + .setDescription(DESCRIPTION) + .setLabels(LABELS) + .build(); + bigquery.create(info); + + Dataset dataset = bigquery.getDataset(DatasetId.of(timeTravelDataset)); + // In the backend, BigQuery sets the default Time Travel Window to be 168 hours (7 days). + assertEquals(MAX_TIME_TRAVEL_HOURS_DEFAULT, dataset.getMaxTimeTravelHours()); + + RemoteBigQueryHelper.forceDelete(bigquery, timeTravelDataset); + } + @Test public void testCreateDatasetWithDefaultCollation() { String collationDataset = RemoteBigQueryHelper.generateDatasetName(); @@ -3194,6 +3235,42 @@ public void testTimestamp() throws InterruptedException { } } + @Test + public void testLosslessTimestamp() throws InterruptedException { + String query = "SELECT TIMESTAMP '2022-01-24T23:54:25.095574Z'"; + long expectedTimestamp = 1643068465095574L; + + TableResult result = + bigquery.query( + QueryJobConfiguration.newBuilder(query) + .setDefaultDataset(DatasetId.of(DATASET)) + .build()); + assertNotNull(result.getJobId()); + for (FieldValueList row : result.getValues()) { + FieldValue timeStampCell = row.get(0); + assertFalse(timeStampCell.getUseInt64Timestamps()); + assertEquals(expectedTimestamp, timeStampCell.getTimestampValue()); + } + + // Create new BQ object to toggle lossless timestamps without affecting + // other tests. + RemoteBigQueryHelper bigqueryHelper = RemoteBigQueryHelper.create(); + BigQuery bigqueryLossless = bigqueryHelper.getOptions().getService(); + bigqueryLossless.getOptions().setUseInt64Timestamps(true); + + TableResult resultLossless = + bigqueryLossless.query( + QueryJobConfiguration.newBuilder(query) + .setDefaultDataset(DatasetId.of(DATASET)) + .build()); + assertNotNull(resultLossless.getJobId()); + for (FieldValueList row : resultLossless.getValues()) { + FieldValue timeStampCellLossless = row.get(0); + assertTrue(timeStampCellLossless.getUseInt64Timestamps()); + assertEquals(expectedTimestamp, timeStampCellLossless.getTimestampValue()); + } + } + /* TODO(prasmish): replicate the entire test case for executeSelect */ @Test public void testQuery() throws InterruptedException { @@ -5306,7 +5383,7 @@ public void testCreateAndGetJob() throws InterruptedException, TimeoutException assertEquals(createdJob.getSelfLink(), remoteJob.getSelfLink()); assertEquals(createdJob.getUserEmail(), remoteJob.getUserEmail()); - Job completedJob = remoteJob.waitFor(RetryOption.totalTimeout(Duration.ofMinutes(1))); + Job completedJob = remoteJob.waitFor(RetryOption.totalTimeoutDuration(Duration.ofMinutes(1))); assertNotNull(completedJob); assertNull(completedJob.getStatus().getError()); assertTrue(createdTable.delete()); @@ -5374,8 +5451,8 @@ public void testCreateAndGetJobWithSelectedFields() assertNull(remoteJob.getUserEmail()); Job completedJob = remoteJob.waitFor( - RetryOption.initialRetryDelay(Duration.ofSeconds(1)), - RetryOption.totalTimeout(Duration.ofMinutes(1))); + RetryOption.initialRetryDelayDuration(Duration.ofSeconds(1)), + RetryOption.totalTimeoutDuration(Duration.ofMinutes(1))); assertNotNull(completedJob); assertTrue(createdTable.delete()); assertNull(completedJob.getStatus().getError()); diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelperTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelperTest.java index 9b2ef4804f..5aadd11e33 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelperTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelperTest.java @@ -25,12 +25,12 @@ import com.google.cloud.http.HttpTransportOptions; import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.time.Duration; import java.util.concurrent.ExecutionException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import org.threeten.bp.Duration; @RunWith(MockitoJUnitRunner.class) public class RemoteBigQueryHelperTest { @@ -83,8 +83,8 @@ public void testCreateFromStream() { assertEquals(60000, ((HttpTransportOptions) options.getTransportOptions()).getConnectTimeout()); assertEquals(60000, ((HttpTransportOptions) options.getTransportOptions()).getReadTimeout()); assertEquals(10, options.getRetrySettings().getMaxAttempts()); - assertEquals(Duration.ofMillis(30000), options.getRetrySettings().getMaxRetryDelay()); - assertEquals(Duration.ofMillis(120000), options.getRetrySettings().getTotalTimeout()); - assertEquals(Duration.ofMillis(250), options.getRetrySettings().getInitialRetryDelay()); + assertEquals(Duration.ofMillis(30000), options.getRetrySettings().getMaxRetryDelayDuration()); + assertEquals(Duration.ofMillis(120000), options.getRetrySettings().getTotalTimeoutDuration()); + assertEquals(Duration.ofMillis(250), options.getRetrySettings().getInitialRetryDelayDuration()); } } diff --git a/pom.xml b/pom.xml index 31168bf894..6fd0cd64e7 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-bigquery-parent pom - 2.43.3 + 2.45.0 BigQuery Parent https://github.com/googleapis/java-bigquery @@ -14,7 +14,7 @@ com.google.cloud sdk-platform-java-config - 3.39.0 + 3.41.0 @@ -54,7 +54,7 @@ UTF-8 github google-cloud-bigquery-parent - v2-rev20241013-2.0.0 + v2-rev20241115-2.0.0 @@ -71,7 +71,7 @@ com.google.cloud google-cloud-bigquerystorage-bom - 3.10.2 + 3.11.0 pom import @@ -79,7 +79,7 @@ com.google.cloud google-cloud-datacatalog-bom - 1.58.0 + 1.60.0 pom import @@ -93,7 +93,7 @@ com.google.cloud google-cloud-bigquery - 2.43.3 + 2.45.0 @@ -137,19 +137,19 @@ com.google.cloud google-cloud-storage - 2.44.1 + 2.46.0 test com.google.cloud google-cloud-bigqueryconnection - 2.54.0 + 2.56.0 test com.google.api.grpc proto-google-cloud-bigqueryconnection-v1 - 2.54.0 + 2.56.0 test diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 667606a0a4..78034de310 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -45,7 +45,7 @@ com.google.cloud google-cloud-bigquery - 2.43.1 + 2.44.0 @@ -63,13 +63,13 @@ com.google.cloud google-cloud-bigtable - 2.45.1 + 2.50.0 test com.google.cloud google-cloud-bigqueryconnection - 2.54.0 + 2.56.0 test diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 358fb3bb4e..d6700af907 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -44,7 +44,7 @@ com.google.cloud google-cloud-bigquery - 2.43.3 + 2.45.0 @@ -61,13 +61,13 @@ com.google.cloud google-cloud-bigtable - 2.45.1 + 2.50.0 test com.google.cloud google-cloud-bigqueryconnection - 2.54.0 + 2.56.0 test diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 68190cb051..f9af31307b 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -79,13 +79,13 @@ com.google.cloud google-cloud-bigtable - 2.45.1 + 2.50.0 test com.google.cloud google-cloud-bigqueryconnection - 2.54.0 + 2.56.0 test diff --git a/versions.txt b/versions.txt index aa33dddd58..13c2e61d78 100644 --- a/versions.txt +++ b/versions.txt @@ -1,4 +1,4 @@ # Format: # module:released-version:current-version -google-cloud-bigquery:2.43.3:2.43.3 \ No newline at end of file +google-cloud-bigquery:2.45.0:2.45.0 \ No newline at end of file