From 6c2e733feed1486300fdd5411af49131d40b2235 Mon Sep 17 00:00:00 2001 From: stephwang Date: Thu, 10 Feb 2022 19:10:19 -0500 Subject: [PATCH 1/4] feat: add Interval type support Fixes b/208051516 --- .../cloud/bigquery/LegacySQLTypeName.java | 3 + .../cloud/bigquery/QueryParameterValue.java | 14 ++++- .../cloud/bigquery/StandardSQLTypeName.java | 6 +- .../bigquery/QueryParameterValueTest.java | 12 ++++ .../cloud/bigquery/it/ITBigQueryTest.java | 56 +++++++++++++++++++ 5 files changed, 88 insertions(+), 3 deletions(-) diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LegacySQLTypeName.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LegacySQLTypeName.java index 1c2a3d884..944df2fb0 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LegacySQLTypeName.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LegacySQLTypeName.java @@ -96,6 +96,9 @@ public LegacySQLTypeName apply(String constant) { /** Represents JSON data */ public static final LegacySQLTypeName JSON = type.createAndRegister("JSON").setStandardType(StandardSQLTypeName.JSON); + /** Represents duration or amount of time. */ + public static final LegacySQLTypeName INTERVAL = + type.createAndRegister("INTERVAL").setStandardType(StandardSQLTypeName.INTERVAL); private static Map standardToLegacyMap = new HashMap<>(); 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 f7f7e384f..3a7afd4d0 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 @@ -63,6 +63,7 @@ *
  • BigDecimal: StandardSQLTypeName.NUMERIC *
  • BigNumeric: StandardSQLTypeName.BIGNUMERIC *
  • JSON: StandardSQLTypeName.JSON + *
  • INTERVAL: StandardSQLTypeName.INTERVAL * * *

    No other types are supported through that entry point. The other types can be created by @@ -308,12 +309,21 @@ public static QueryParameterValue time(String value) { /** * Creates a {@code QueryParameterValue} object with a type of DATETIME. Must be in the format - * "yyyy-MM-dd HH:mm:ss.SSSSSS", e.g. ""2014-08-19 12:41:35.220000". + * "yyyy-MM-dd HH:mm:ss.SSSSSS", e.g. "2014-08-19 12:41:35.220000". */ public static QueryParameterValue dateTime(String value) { return of(value, StandardSQLTypeName.DATETIME); } + /** + * Creates a {@code QueryParameterValue} object with a type of INTERVAL. Must be in the canonical + * format "[sign]Y-M [sign]D [sign]H:M:S[.F]", e.g. "123-7 -19 0:24:12.000006" or ISO 8601 + * duration format, e.g. "P123Y7M-19DT0H24M12.000006S" + */ + public static QueryParameterValue interval(String value) { + return of(value, StandardSQLTypeName.INTERVAL); + } + /** * Creates a {@code QueryParameterValue} object with a type of ARRAY, and an array element type * based on the given class. @@ -408,6 +418,8 @@ private static String valueToStringOrNull(T value, StandardSQLTypeName type) return value.toString(); case JSON: if (value instanceof String || value instanceof JsonObject) return value.toString(); + case INTERVAL: + if (value instanceof String) return value.toString(); break; case STRUCT: throw new IllegalArgumentException("Cannot convert STRUCT to String value"); diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java index 57152a2a6..1081fc5d6 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java @@ -57,6 +57,8 @@ public enum StandardSQLTypeName { DATETIME, /** Represents a set of geographic points, represented as a Well Known Text (WKT) string. */ GEOGRAPHY, - /** Represents JSON data */ - JSON + /** Represents JSON data. */ + JSON, + /** Represents duration or amount of time. */ + INTERVAL } 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 679b6ec5c..cd405c2fe 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 @@ -212,6 +212,18 @@ public void testJson() { assertThat(value1.getArrayType()).isNull(); } + @Test + public void testInterval() { + QueryParameterValue value = QueryParameterValue.interval("123-7 -19 0:24:12.000006"); + QueryParameterValue value1 = QueryParameterValue.interval("P123Y7M-19DT0H24M12.000006S"); + assertThat(value.getValue()).isEqualTo("123-7 -19 0:24:12.000006"); + assertThat(value1.getValue()).isEqualTo("P123Y7M-19DT0H24M12.000006S"); + assertThat(value.getType()).isEqualTo(StandardSQLTypeName.INTERVAL); + assertThat(value1.getType()).isEqualTo(StandardSQLTypeName.INTERVAL); + assertThat(value.getArrayType()).isNull(); + assertThat(value1.getArrayType()).isNull(); + } + @Test public void testBytes() { QueryParameterValue value = QueryParameterValue.bytes(new byte[] {1, 3}); 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 b8051122f..7fd54998c 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 @@ -816,6 +816,62 @@ public void testJsonType() throws InterruptedException { } } + @Test + public void testIntervalType() throws InterruptedException { + String tableName = "test_create_table_intervaltype"; + TableId tableId = TableId.of(DATASET, tableName); + Schema schema = Schema.of(Field.of("intervalField", StandardSQLTypeName.INTERVAL)); + StandardTableDefinition standardTableDefinition = StandardTableDefinition.of(schema); + try { + // Create a table with a JSON column + Table createdTable = bigquery.create(TableInfo.of(tableId, standardTableDefinition)); + assertNotNull(createdTable); + + // Insert 3 rows of Interval data into the Interval column + Map intervalRow1 = + Collections.singletonMap("intervalField", "123-7 -19 0:24:12.000006"); + Map intervalRow2 = + Collections.singletonMap("intervalField", "P123Y7M-19DT0H24M12.000006S"); + + InsertAllRequest request = + InsertAllRequest.newBuilder(tableId).addRow(intervalRow1).addRow(intervalRow2).build(); + InsertAllResponse response = bigquery.insertAll(request); + assertFalse(response.hasErrors()); + assertEquals(0, response.getInsertErrors().size()); + + // Insert another Interval row parsed from a String with Interval positional query parameter + String dml = "INSERT INTO " + tableId.getTable() + " (intervalField) VALUES(?)"; + QueryParameterValue intervalParameter = + QueryParameterValue.interval("P125Y7M-19DT0H24M12.000006S"); + QueryJobConfiguration dmlQueryJobConfiguration = + QueryJobConfiguration.newBuilder(dml) + .setDefaultDataset(DatasetId.of(DATASET)) + .setUseLegacySql(false) + .addPositionalParameter(intervalParameter) + .build(); + bigquery.query(dmlQueryJobConfiguration); + Page rows = bigquery.listTableData(tableId); + assertEquals(3, Iterables.size(rows.getValues())); + + // Query the Interval column with Interval positional query parameter + String sql = "SELECT intervalField FROM " + tableId.getTable() + " WHERE intervalField = ? "; + QueryParameterValue intervalParameter1 = + QueryParameterValue.interval("P125Y7M-19DT0H24M12.000006S"); + QueryJobConfiguration queryJobConfiguration = + QueryJobConfiguration.newBuilder(sql) + .setDefaultDataset(DatasetId.of(DATASET)) + .setUseLegacySql(false) + .addPositionalParameter(intervalParameter1) + .build(); + TableResult result = bigquery.query(queryJobConfiguration); + for (FieldValueList values : result.iterateAll()) { + assertEquals("125-7 -19 0:24:12.000006", values.get(0).getValue()); + } + } finally { + assertTrue(bigquery.delete(tableId)); + } + } + @Test public void testCreateTableWithConstraints() { String tableName = "test_create_table_with_constraints"; From 6428feaf9b81578edeedd01c25c29a71035a77ba Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 11 Feb 2022 15:44:17 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ec60a4f3..401e18cf0 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies ```Groovy -implementation platform('com.google.cloud:libraries-bom:24.2.0') +implementation platform('com.google.cloud:libraries-bom:24.3.0') implementation 'com.google.cloud:google-cloud-bigquery' ``` From 6afb331decf4b4736e232c8bf4c7c852da873678 Mon Sep 17 00:00:00 2001 From: stephwang Date: Fri, 11 Feb 2022 12:20:27 -0500 Subject: [PATCH 3/4] add threeten-extra PeriodDuration support --- google-cloud-bigquery/pom.xml | 4 ++++ .../cloud/bigquery/QueryParameterValue.java | 8 ++++++- .../cloud/bigquery/it/ITBigQueryTest.java | 21 +++++++++++++++++-- pom.xml | 8 +++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/google-cloud-bigquery/pom.xml b/google-cloud-bigquery/pom.xml index ba2a040e6..acc436d43 100644 --- a/google-cloud-bigquery/pom.xml +++ b/google-cloud-bigquery/pom.xml @@ -85,6 +85,10 @@ com.google.code.gson gson + + org.threeten + threeten-extra + 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 3a7afd4d0..227597bb1 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 @@ -43,6 +43,7 @@ import org.threeten.bp.format.DateTimeFormatter; import org.threeten.bp.format.DateTimeFormatterBuilder; import org.threeten.bp.format.DateTimeParseException; +import org.threeten.extra.PeriodDuration; /** * A value for a QueryParameter along with its type. @@ -324,6 +325,11 @@ public static QueryParameterValue interval(String value) { return of(value, StandardSQLTypeName.INTERVAL); } + /** Creates a {@code QueryParameterValue} object with a type of INTERVAL. */ + public static QueryParameterValue interval(PeriodDuration value) { + return of(value, StandardSQLTypeName.INTERVAL); + } + /** * Creates a {@code QueryParameterValue} object with a type of ARRAY, and an array element type * based on the given class. @@ -419,7 +425,7 @@ private static String valueToStringOrNull(T value, StandardSQLTypeName type) case JSON: if (value instanceof String || value instanceof JsonObject) return value.toString(); case INTERVAL: - if (value instanceof String) return value.toString(); + if (value instanceof String || value instanceof PeriodDuration) return value.toString(); break; case STRUCT: throw new IllegalArgumentException("Cannot convert STRUCT to String value"); 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 7fd54998c..282bef986 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 @@ -130,6 +130,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.time.Instant; +import java.time.Period; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -151,6 +152,7 @@ import org.junit.Test; import org.junit.rules.Timeout; import org.threeten.bp.Duration; +import org.threeten.extra.PeriodDuration; public class ITBigQueryTest { @@ -841,6 +843,7 @@ public void testIntervalType() throws InterruptedException { // Insert another Interval row parsed from a String with Interval positional query parameter String dml = "INSERT INTO " + tableId.getTable() + " (intervalField) VALUES(?)"; + // Parsing from ISO 8610 format String QueryParameterValue intervalParameter = QueryParameterValue.interval("P125Y7M-19DT0H24M12.000006S"); QueryJobConfiguration dmlQueryJobConfiguration = @@ -853,15 +856,29 @@ public void testIntervalType() throws InterruptedException { Page rows = bigquery.listTableData(tableId); assertEquals(3, Iterables.size(rows.getValues())); + // Parsing from threeten-extra PeriodDuration + QueryParameterValue intervalParameter1 = + QueryParameterValue.interval( + PeriodDuration.of(Period.of(1, 2, 25), java.time.Duration.ofHours(8))); + QueryJobConfiguration dmlQueryJobConfiguration1 = + QueryJobConfiguration.newBuilder(dml) + .setDefaultDataset(DatasetId.of(DATASET)) + .setUseLegacySql(false) + .addPositionalParameter(intervalParameter1) + .build(); + bigquery.query(dmlQueryJobConfiguration1); + Page rows1 = bigquery.listTableData(tableId); + assertEquals(4, Iterables.size(rows1.getValues())); + // Query the Interval column with Interval positional query parameter String sql = "SELECT intervalField FROM " + tableId.getTable() + " WHERE intervalField = ? "; - QueryParameterValue intervalParameter1 = + QueryParameterValue intervalParameter2 = QueryParameterValue.interval("P125Y7M-19DT0H24M12.000006S"); QueryJobConfiguration queryJobConfiguration = QueryJobConfiguration.newBuilder(sql) .setDefaultDataset(DatasetId.of(DATASET)) .setUseLegacySql(false) - .addPositionalParameter(intervalParameter1) + .addPositionalParameter(intervalParameter2) .build(); TableResult result = bigquery.query(queryJobConfiguration); for (FieldValueList values : result.iterateAll()) { diff --git a/pom.xml b/pom.xml index 15d25e3a2..e47211209 100644 --- a/pom.xml +++ b/pom.xml @@ -93,12 +93,20 @@ ${google-api-services-bigquery.version} + com.google.code.gson gson 2.8.9 + + + org.threeten + threeten-extra + 1.7.0 + + junit From 1b192e6262e76d0472170f9f4da17b2473a1f859 Mon Sep 17 00:00:00 2001 From: stephwang Date: Fri, 11 Feb 2022 12:24:12 -0500 Subject: [PATCH 4/4] add unit test coverage --- .../google/cloud/bigquery/QueryParameterValueTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 cd405c2fe..6b5368003 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 @@ -27,6 +27,7 @@ import com.google.gson.JsonObject; import java.math.BigDecimal; import java.text.ParseException; +import java.time.Period; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -38,6 +39,7 @@ 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 { @@ -216,12 +218,18 @@ public void testJson() { public void testInterval() { QueryParameterValue value = QueryParameterValue.interval("123-7 -19 0:24:12.000006"); QueryParameterValue value1 = QueryParameterValue.interval("P123Y7M-19DT0H24M12.000006S"); + QueryParameterValue value2 = + QueryParameterValue.interval( + PeriodDuration.of(Period.of(1, 2, 25), java.time.Duration.ofHours(8))); assertThat(value.getValue()).isEqualTo("123-7 -19 0:24:12.000006"); assertThat(value1.getValue()).isEqualTo("P123Y7M-19DT0H24M12.000006S"); + assertThat(value2.getValue()).isEqualTo("P1Y2M25DT8H"); assertThat(value.getType()).isEqualTo(StandardSQLTypeName.INTERVAL); assertThat(value1.getType()).isEqualTo(StandardSQLTypeName.INTERVAL); + assertThat(value2.getType()).isEqualTo(StandardSQLTypeName.INTERVAL); assertThat(value.getArrayType()).isNull(); assertThat(value1.getArrayType()).isNull(); + assertThat(value2.getArrayType()).isNull(); } @Test