Skip to content

Commit fd3d3c5

Browse files
authored
feat: add preview support for default values (#2244)
1 parent 6ae53d1 commit fd3d3c5

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed

google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Field.java

+53
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public TableFieldSchema apply(Field field) {
6262
private final Long maxLength;
6363
private final Long scale;
6464
private final Long precision;
65+
private final String defaultValueExpression;
6566

6667
/**
6768
* Mode for a BigQuery Table field. {@link Mode#NULLABLE} fields can be set to {@code null},
@@ -85,6 +86,7 @@ public static final class Builder {
8586
private Long maxLength;
8687
private Long scale;
8788
private Long precision;
89+
private String defaultValueExpression;
8890

8991
private Builder() {}
9092

@@ -98,6 +100,7 @@ private Builder(Field field) {
98100
this.maxLength = field.maxLength;
99101
this.scale = field.scale;
100102
this.precision = field.precision;
103+
this.defaultValueExpression = field.defaultValueExpression;
101104
}
102105

103106
/**
@@ -245,6 +248,43 @@ public Builder setPrecision(Long precision) {
245248
return this;
246249
}
247250

251+
/**
252+
* DefaultValueExpression is used to specify the default value of a field using a SQL
253+
* expression. It can only be set for top level fields (columns).
254+
*
255+
* <p>You can use struct or array expression to specify default value for the entire struct or
256+
* array. The valid SQL expressions are:
257+
*
258+
* <ul>
259+
* <ul>
260+
* <li>Literals for all data types, including STRUCT and ARRAY.
261+
* </ul>
262+
* <ul>
263+
* <li>The following functions:
264+
* <ul>
265+
* <li>CURRENT_TIMESTAMP
266+
* <li>CURRENT_TIME
267+
* <li>CURRENT_DATE
268+
* <li>CURRENT_DATETIME
269+
* <li>GENERATE_UUID
270+
* <li>RAND
271+
* <li>SESSION_USER
272+
* <li>ST_GEOGPOINT
273+
* </ul>
274+
* </ul>
275+
* <ul>
276+
* <li>Struct or array composed with the above allowed functions, for example:
277+
* <ul>
278+
* <li>"[CURRENT_DATE(), DATE '2020-01-01']"
279+
* </ul>
280+
* </ul>
281+
* </ul>
282+
*/
283+
public Builder setDefaultValueExpression(String defaultValueExpression) {
284+
this.defaultValueExpression = defaultValueExpression;
285+
return this;
286+
}
287+
248288
/** Creates a {@code Field} object. */
249289
public Field build() {
250290
return new Field(this);
@@ -261,6 +301,7 @@ private Field(Builder builder) {
261301
this.maxLength = builder.maxLength;
262302
this.scale = builder.scale;
263303
this.precision = builder.precision;
304+
this.defaultValueExpression = builder.defaultValueExpression;
264305
}
265306

266307
/** Returns the field name. */
@@ -311,6 +352,11 @@ public Long getPrecision() {
311352
return precision;
312353
}
313354

355+
/** Return the default value of the field. */
356+
public String getDefaultValueExpression() {
357+
return defaultValueExpression;
358+
}
359+
314360
/**
315361
* Returns the list of sub-fields if {@link #getType()} is a {@link LegacySQLTypeName#RECORD}.
316362
* Returns {@code null} otherwise.
@@ -335,6 +381,7 @@ public String toString() {
335381
.add("maxLength", maxLength)
336382
.add("scale", scale)
337383
.add("precision", precision)
384+
.add("defaultValueExpression", defaultValueExpression)
338385
.toString();
339386
}
340387

@@ -414,6 +461,9 @@ TableFieldSchema toPb() {
414461
if (precision != null) {
415462
fieldSchemaPb.setPrecision(precision);
416463
}
464+
if (defaultValueExpression != null) {
465+
fieldSchemaPb.setDefaultValueExpression(defaultValueExpression);
466+
}
417467
if (getSubFields() != null) {
418468
List<TableFieldSchema> fieldsPb = Lists.transform(getSubFields(), TO_PB_FUNCTION);
419469
fieldSchemaPb.setFields(fieldsPb);
@@ -442,6 +492,9 @@ static Field fromPb(TableFieldSchema fieldSchemaPb) {
442492
if (fieldSchemaPb.getPrecision() != null) {
443493
fieldBuilder.setPrecision(fieldSchemaPb.getPrecision());
444494
}
495+
if (fieldSchemaPb.getDefaultValueExpression() != null) {
496+
fieldBuilder.setDefaultValueExpression(fieldSchemaPb.getDefaultValueExpression());
497+
}
445498
FieldList subFields =
446499
fieldSchemaPb.getFields() != null
447500
? FieldList.of(Lists.transform(fieldSchemaPb.getFields(), FROM_PB_FUNCTION))

google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldTest.java

+6
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,13 @@ public class FieldTest {
3939
private static final String FIELD_DESCRIPTION1 = "FieldDescription1";
4040
private static final String FIELD_DESCRIPTION2 = "FieldDescription2";
4141
private static final String FIELD_DESCRIPTION3 = "FieldDescription3";
42+
private static final String FIELD_DEFAULT_VALUE_EXPRESSION1 =
43+
"This is default value for this field";
4244
private static final Field FIELD_SCHEMA1 =
4345
Field.newBuilder(FIELD_NAME1, FIELD_TYPE1)
4446
.setMode(FIELD_MODE1)
4547
.setDescription(FIELD_DESCRIPTION1)
48+
.setDefaultValueExpression(FIELD_DEFAULT_VALUE_EXPRESSION1)
4649
.build();
4750
private static final Field FIELD_SCHEMA2 =
4851
Field.newBuilder(FIELD_NAME2, FIELD_TYPE2)
@@ -60,6 +63,7 @@ public class FieldTest {
6063
Field.newBuilder(FIELD_NAME1, StandardSQLTypeName.STRING)
6164
.setMode(FIELD_MODE1)
6265
.setDescription(FIELD_DESCRIPTION1)
66+
.setDefaultValueExpression(FIELD_DEFAULT_VALUE_EXPRESSION1)
6367
.build();
6468
private static final Field STANDARD_FIELD_SCHEMA2 =
6569
Field.newBuilder(FIELD_NAME2, StandardSQLTypeName.INT64)
@@ -137,6 +141,7 @@ public void testBuilder() {
137141
assertEquals(FIELD_TYPE1, FIELD_SCHEMA1.getType());
138142
assertEquals(FIELD_MODE1, FIELD_SCHEMA1.getMode());
139143
assertEquals(FIELD_DESCRIPTION1, FIELD_SCHEMA1.getDescription());
144+
assertEquals(FIELD_DEFAULT_VALUE_EXPRESSION1, FIELD_SCHEMA1.getDefaultValueExpression());
140145
assertEquals(null, FIELD_SCHEMA1.getSubFields());
141146
assertEquals(FIELD_NAME3, FIELD_SCHEMA3.getName());
142147
assertEquals(FIELD_TYPE3, FIELD_SCHEMA3.getType());
@@ -151,6 +156,7 @@ public void testBuilderWithStandardSQLTypeName() {
151156
assertEquals(FIELD_TYPE1, STANDARD_FIELD_SCHEMA1.getType());
152157
assertEquals(FIELD_MODE1, STANDARD_FIELD_SCHEMA1.getMode());
153158
assertEquals(FIELD_DESCRIPTION1, STANDARD_FIELD_SCHEMA1.getDescription());
159+
assertEquals(FIELD_DEFAULT_VALUE_EXPRESSION1, FIELD_SCHEMA1.getDefaultValueExpression());
154160
assertEquals(null, STANDARD_FIELD_SCHEMA1.getSubFields());
155161
assertEquals(FIELD_NAME3, STANDARD_FIELD_SCHEMA3.getName());
156162
assertEquals(FIELD_TYPE3, STANDARD_FIELD_SCHEMA3.getType());

google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java

+63
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import com.google.cloud.bigquery.FormatOptions;
7373
import com.google.cloud.bigquery.HivePartitioningOptions;
7474
import com.google.cloud.bigquery.InsertAllRequest;
75+
import com.google.cloud.bigquery.InsertAllRequest.RowToInsert;
7576
import com.google.cloud.bigquery.InsertAllResponse;
7677
import com.google.cloud.bigquery.Job;
7778
import com.google.cloud.bigquery.JobId;
@@ -1176,6 +1177,68 @@ public void testCreateTableWithConstraints() {
11761177
bigquery.delete(tableId);
11771178
}
11781179

1180+
@Test
1181+
public void testCreateTableWithDefaultValueExpression() {
1182+
String tableName = "test_create_table_with_default_value_expression";
1183+
TableId tableId = TableId.of(DATASET, tableName);
1184+
Field stringFieldWithDefaultValueExpression =
1185+
Field.newBuilder("stringFieldWithDefaultValueExpression", StandardSQLTypeName.STRING)
1186+
.setMode(Field.Mode.NULLABLE)
1187+
.setDescription("String field with default value expression")
1188+
.setDefaultValueExpression("'FOO'")
1189+
.setMaxLength(150L)
1190+
.build();
1191+
Field timestampFieldWithDefaultValueExpression =
1192+
Field.newBuilder("timestampFieldWithDefaultValueExpression", StandardSQLTypeName.TIMESTAMP)
1193+
.setMode(Field.Mode.NULLABLE)
1194+
.setDescription("Timestamp field with default value expression")
1195+
.setDefaultValueExpression("CURRENT_TIMESTAMP")
1196+
.build();
1197+
Schema schema =
1198+
Schema.of(stringFieldWithDefaultValueExpression, timestampFieldWithDefaultValueExpression);
1199+
StandardTableDefinition tableDefinition =
1200+
StandardTableDefinition.newBuilder().setSchema(schema).build();
1201+
1202+
// Create table with fields that have default value expression
1203+
Table createdTable = bigquery.create(TableInfo.of(tableId, tableDefinition));
1204+
assertNotNull(createdTable);
1205+
1206+
// Fetch the created table and its metadata
1207+
// to verify default value expression is assigned to fields
1208+
Table remoteTable = bigquery.getTable(DATASET, tableName);
1209+
Schema remoteSchema = remoteTable.<StandardTableDefinition>getDefinition().getSchema();
1210+
assertEquals(schema, remoteSchema);
1211+
FieldList fieldList = remoteSchema.getFields();
1212+
for (Field field : fieldList) {
1213+
if (field.getName().equals("timestampFieldWithDefaultValueExpression")) {
1214+
assertEquals("CURRENT_TIMESTAMP", field.getDefaultValueExpression());
1215+
}
1216+
if (field.getName().equals("stringFieldWithDefaultValueExpression")) {
1217+
assertEquals("'FOO'", field.getDefaultValueExpression());
1218+
}
1219+
}
1220+
1221+
// Insert value into the created table
1222+
// to verify default values are inserted when value is missing
1223+
String rowId1 = "rowId1";
1224+
String rowId2 = "rowId2";
1225+
List<RowToInsert> rows = new ArrayList<>();
1226+
Map<String, Object> row1 = new HashMap<>();
1227+
row1.put("timestampFieldWithDefaultValueExpression", "2022-08-22 00:45:12 UTC");
1228+
Map<String, Object> row2 = new HashMap<>();
1229+
row2.put("timestampFieldWithDefaultValueExpression", "2022-08-23 00:44:33 UTC");
1230+
rows.add(RowToInsert.of(rowId1, row1));
1231+
rows.add(RowToInsert.of(rowId2, row2));
1232+
InsertAllResponse response1 = remoteTable.insert(rows);
1233+
1234+
TableResult tableData = bigquery.listTableData(DATASET, tableName, schema);
1235+
String insertedField = "stringFieldWithDefaultValueExpression";
1236+
for (FieldValueList row : tableData.iterateAll()) {
1237+
assertEquals("FOO", row.get(insertedField).getValue());
1238+
}
1239+
bigquery.delete(tableId);
1240+
}
1241+
11791242
@Test
11801243
public void testCreateAndUpdateTableWithPolicyTags() throws IOException {
11811244
// Set up policy tags in the datacatalog service

0 commit comments

Comments
 (0)