diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Field.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Field.java index 7496530dd..9fbc2ab91 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Field.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Field.java @@ -64,6 +64,7 @@ public TableFieldSchema apply(Field field) { private final Long precision; private final String defaultValueExpression; private final String collation; + private final FieldElementType rangeElementType; /** * Mode for a BigQuery Table field. {@link Mode#NULLABLE} fields can be set to {@code null}, @@ -89,6 +90,7 @@ public static final class Builder { private Long precision; private String defaultValueExpression; private String collation; + private FieldElementType rangeElementType; private Builder() {} @@ -104,6 +106,7 @@ private Builder(Field field) { this.precision = field.precision; this.defaultValueExpression = field.defaultValueExpression; this.collation = field.collation; + this.rangeElementType = field.rangeElementType; } /** @@ -292,6 +295,12 @@ public Builder setCollation(String collation) { return this; } + /** Optional. Field range element type can be set only when the type of field is RANGE. */ + public Builder setRangeElementType(FieldElementType rangeElementType) { + this.rangeElementType = rangeElementType; + return this; + } + /** Creates a {@code Field} object. */ public Field build() { return new Field(this); @@ -310,6 +319,7 @@ private Field(Builder builder) { this.precision = builder.precision; this.defaultValueExpression = builder.defaultValueExpression; this.collation = builder.collation; + this.rangeElementType = builder.rangeElementType; } /** Returns the field name. */ @@ -369,6 +379,11 @@ public String getCollation() { return collation; } + /** Return the range element type the field. */ + public FieldElementType getRangeElementType() { + return rangeElementType; + } + /** * Returns the list of sub-fields if {@link #getType()} is a {@link LegacySQLTypeName#RECORD}. * Returns {@code null} otherwise. @@ -395,12 +410,13 @@ public String toString() { .add("precision", precision) .add("defaultValueExpression", defaultValueExpression) .add("collation", collation) + .add("rangeElementType", rangeElementType) .toString(); } @Override public int hashCode() { - return Objects.hash(name, type, mode, description, policyTags); + return Objects.hash(name, type, mode, description, policyTags, rangeElementType); } @Override @@ -484,6 +500,9 @@ TableFieldSchema toPb() { if (collation != null) { fieldSchemaPb.setCollation(collation); } + if (rangeElementType != null) { + fieldSchemaPb.setRangeElementType(rangeElementType.toPb()); + } return fieldSchemaPb; } @@ -519,6 +538,10 @@ static Field fromPb(TableFieldSchema fieldSchemaPb) { if (fieldSchemaPb.getCollation() != null) { fieldBuilder.setCollation(fieldSchemaPb.getCollation()); } + if (fieldSchemaPb.getRangeElementType() != null) { + fieldBuilder.setRangeElementType( + FieldElementType.fromPb(fieldSchemaPb.getRangeElementType())); + } return fieldBuilder.build(); } } diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldElementType.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldElementType.java new file mode 100644 index 000000000..43446e1d0 --- /dev/null +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldElementType.java @@ -0,0 +1,63 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigquery; + +import com.google.api.services.bigquery.model.TableFieldSchema; +import com.google.auto.value.AutoValue; +import java.io.Serializable; +import javax.annotation.Nullable; + +@AutoValue +public abstract class FieldElementType implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * The subtype of the RANGE, if the field type is RANGE. + * + * @return value or {@code null} for none + */ + @Nullable + public abstract String getType(); + + public abstract FieldElementType.Builder toBuilder(); + + @AutoValue.Builder + public abstract static class Builder { + + public abstract FieldElementType.Builder setType(String type); + + public abstract FieldElementType build(); + } + + public static Builder newBuilder() { + return new AutoValue_FieldElementType.Builder(); + } + + TableFieldSchema.RangeElementType toPb() { + TableFieldSchema.RangeElementType rangeElementTypePb = new TableFieldSchema.RangeElementType(); + rangeElementTypePb.setType(getType()); + return rangeElementTypePb; + } + + static FieldElementType fromPb(TableFieldSchema.RangeElementType rangeElementTypePb) { + // Treat a FieldElementType message without a Type subfield as invalid. + if (rangeElementTypePb.getType() != null) { + return newBuilder().setType(rangeElementTypePb.getType()).build(); + } + return null; + } +} 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 944df2fb0..dec2583e9 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 @@ -100,6 +100,10 @@ public LegacySQLTypeName apply(String constant) { public static final LegacySQLTypeName INTERVAL = type.createAndRegister("INTERVAL").setStandardType(StandardSQLTypeName.INTERVAL); + /** Represents a contiguous range of values. */ + public static final LegacySQLTypeName RANGE = + type.createAndRegister("RANGE").setStandardType(StandardSQLTypeName.RANGE); + private static Map standardToLegacyMap = new HashMap<>(); static { 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 1081fc5d6..1f70183cd 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 @@ -60,5 +60,7 @@ public enum StandardSQLTypeName { /** Represents JSON data. */ JSON, /** Represents duration or amount of time. */ - INTERVAL + INTERVAL, + /** Represents a contiguous range of values. */ + RANGE } diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldElementTypeTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldElementTypeTest.java new file mode 100644 index 000000000..9b4590892 --- /dev/null +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldElementTypeTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.bigquery; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class FieldElementTypeTest { + private static final FieldElementType FIELD_ELEMENT_TYPE = + FieldElementType.newBuilder().setType("DATE").build(); + + @Test + public void testToBuilder() { + compareFieldElementType(FIELD_ELEMENT_TYPE, FIELD_ELEMENT_TYPE.toBuilder().build()); + } + + @Test + public void testBuilder() { + assertEquals("DATE", FIELD_ELEMENT_TYPE.getType()); + } + + @Test + public void testFromAndPb() { + assertEquals(FIELD_ELEMENT_TYPE, FieldElementType.fromPb(FIELD_ELEMENT_TYPE.toPb())); + } + + private void compareFieldElementType(FieldElementType expected, FieldElementType value) { + assertEquals(expected.getType(), value.getType()); + assertEquals(expected.hashCode(), value.hashCode()); + assertEquals(expected.toString(), value.toString()); + } +} 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 9a0e3e107..d8d673360 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 @@ -72,6 +72,7 @@ import com.google.cloud.bigquery.ExtractJobConfiguration; import com.google.cloud.bigquery.Field; import com.google.cloud.bigquery.Field.Mode; +import com.google.cloud.bigquery.FieldElementType; import com.google.cloud.bigquery.FieldList; import com.google.cloud.bigquery.FieldValue; import com.google.cloud.bigquery.FieldValue.Attribute; @@ -1246,6 +1247,25 @@ public void testIntervalType() throws InterruptedException { } } + @Test + public void testRangeType() throws InterruptedException { + String tableName = "test_create_table_rangetype"; + TableId tableId = TableId.of(DATASET, tableName); + Schema schema = + Schema.of( + Field.newBuilder("rangeField", StandardSQLTypeName.RANGE) + .setRangeElementType(FieldElementType.newBuilder().setType("DATETIME").build()) + .build()); + StandardTableDefinition standardTableDefinition = StandardTableDefinition.of(schema); + try { + // Create a table with a RANGE column. + Table createdTable = bigquery.create(TableInfo.of(tableId, standardTableDefinition)); + assertNotNull(createdTable); + } finally { + assertTrue(bigquery.delete(tableId)); + } + } + @Test public void testCreateTableWithConstraints() { String tableName = "test_create_table_with_constraints";