Skip to content
This repository was archived by the owner on Mar 23, 2026. It is now read-only.

Commit 8207a50

Browse files
committed
Merge branch 'main' into PR #3942 to update
2 parents 9227a70 + 5a51a51 commit 8207a50

11 files changed

Lines changed: 424 additions & 85 deletions

File tree

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,7 +1731,10 @@ public TableDataList call() throws IOException {
17311731
new PageImpl<>(
17321732
new TableDataPageFetcher(tableId, schema, serviceOptions, cursor, pageOptionMap),
17331733
cursor,
1734-
transformTableData(result.getRows(), schema, serviceOptions.getUseInt64Timestamps())),
1734+
transformTableData(
1735+
result.getRows(),
1736+
schema,
1737+
serviceOptions.getDataFormatOptions().useInt64Timestamp())),
17351738
result.getTotalRows());
17361739
} catch (BigQueryRetryHelperException e) {
17371740
throw BigQueryException.translateAndThrow(e);
@@ -2007,7 +2010,9 @@ public com.google.api.services.bigquery.model.QueryResponse call()
20072010
new QueryPageFetcher(jobId, schema, getOptions(), cursor, optionMap(options)),
20082011
cursor,
20092012
transformTableData(
2010-
results.getRows(), schema, getOptions().getUseInt64Timestamps())))
2013+
results.getRows(),
2014+
schema,
2015+
getOptions().getDataFormatOptions().useInt64Timestamp())))
20112016
.setJobId(jobId)
20122017
.setQueryId(results.getQueryId())
20132018
.build();
@@ -2021,7 +2026,9 @@ public com.google.api.services.bigquery.model.QueryResponse call()
20212026
new TableDataPageFetcher(null, schema, getOptions(), null, optionMap(options)),
20222027
null,
20232028
transformTableData(
2024-
results.getRows(), schema, getOptions().getUseInt64Timestamps())))
2029+
results.getRows(),
2030+
schema,
2031+
getOptions().getDataFormatOptions().useInt64Timestamp())))
20252032
// Return the JobID of the successful job
20262033
.setJobId(
20272034
results.getJobReference() != null ? JobId.fromPb(results.getJobReference()) : null)
@@ -2066,10 +2073,9 @@ && getOptions().getOpenTelemetryTracer() != null) {
20662073
}
20672074
try (Scope queryScope = querySpan != null ? querySpan.makeCurrent() : null) {
20682075
// If all parameters passed in configuration are supported by the query() method on the
2069-
// backend,
2070-
// put on fast path
2076+
// backend, put on fast path
20712077
QueryRequestInfo requestInfo =
2072-
new QueryRequestInfo(configuration, getOptions().getUseInt64Timestamps());
2078+
new QueryRequestInfo(configuration, getOptions().getDataFormatOptions());
20732079
if (requestInfo.isFastQuerySupported(jobId)) {
20742080
// Be careful when setting the projectID in JobId, if a projectID is specified in the JobId,
20752081
// the job created by the query method will use that project. This may cause the query to

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

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.google.cloud.bigquery;
1818

1919
import com.google.api.core.BetaApi;
20+
import com.google.api.core.ObsoleteApi;
2021
import com.google.api.gax.retrying.ResultRetryAlgorithm;
2122
import com.google.cloud.ServiceDefaults;
2223
import com.google.cloud.ServiceOptions;
@@ -26,6 +27,7 @@
2627
import com.google.cloud.bigquery.spi.BigQueryRpcFactory;
2728
import com.google.cloud.bigquery.spi.v2.HttpBigQueryRpc;
2829
import com.google.cloud.http.HttpTransportOptions;
30+
import com.google.common.base.Preconditions;
2931
import com.google.common.collect.ImmutableSet;
3032
import io.opentelemetry.api.trace.Tracer;
3133
import java.util.Set;
@@ -41,6 +43,7 @@ public class BigQueryOptions extends ServiceOptions<BigQuery, BigQueryOptions> {
4143
// set the option ThrowNotFound when you want to throw the exception when the value not found
4244
private boolean setThrowNotFound;
4345
private boolean useInt64Timestamps;
46+
private DataFormatOptions dataFormatOptions;
4447
private JobCreationMode defaultJobCreationMode = JobCreationMode.JOB_CREATION_MODE_UNSPECIFIED;
4548
private boolean enableOpenTelemetryTracing;
4649
private Tracer openTelemetryTracer;
@@ -70,6 +73,7 @@ public static class Builder extends ServiceOptions.Builder<BigQuery, BigQueryOpt
7073

7174
private String location;
7275
private boolean useInt64Timestamps;
76+
private DataFormatOptions dataFormatOptions;
7377
private boolean enableOpenTelemetryTracing;
7478
private Tracer openTelemetryTracer;
7579
private ResultRetryAlgorithm<?> resultRetryAlgorithm;
@@ -94,11 +98,32 @@ public Builder setLocation(String location) {
9498
return this;
9599
}
96100

101+
/**
102+
* This setter is marked as Obsolete. Prefer {@link #setDataFormatOptions(DataFormatOptions)} to
103+
* set the int64timestamp configuration instead.
104+
*
105+
* <p>If useInt64Timestamps value is set in here and via DataFormatOptions, the
106+
* DataFormatOptions configuration value is used.
107+
*
108+
* <p>{@code DataFormatOptions.newBuilder().setUseInt64Timestamp(...).build()}
109+
*/
110+
@ObsoleteApi("Use setDataFormatOptions(DataFormatOptions) instead")
97111
public Builder setUseInt64Timestamps(boolean useInt64Timestamps) {
98112
this.useInt64Timestamps = useInt64Timestamps;
99113
return this;
100114
}
101115

116+
/**
117+
* Set the format options for the BigQuery data types
118+
*
119+
* @param dataFormatOptions Configuration of the formatting options
120+
*/
121+
public Builder setDataFormatOptions(DataFormatOptions dataFormatOptions) {
122+
Preconditions.checkNotNull(dataFormatOptions, "DataFormatOptions cannot be null");
123+
this.dataFormatOptions = dataFormatOptions;
124+
return this;
125+
}
126+
102127
/**
103128
* Enables OpenTelemetry tracing functionality for this BigQuery instance
104129
*
@@ -143,6 +168,15 @@ private BigQueryOptions(Builder builder) {
143168
} else {
144169
this.resultRetryAlgorithm = BigQueryBaseService.DEFAULT_BIGQUERY_EXCEPTION_HANDLER;
145170
}
171+
172+
// If dataFormatOptions is not set, then create a new instance and set it with the
173+
// useInt64Timestamps configured in BigQueryOptions
174+
if (builder.dataFormatOptions == null) {
175+
this.dataFormatOptions =
176+
DataFormatOptions.newBuilder().useInt64Timestamp(builder.useInt64Timestamps).build();
177+
} else {
178+
this.dataFormatOptions = builder.dataFormatOptions;
179+
}
146180
}
147181

148182
private static class BigQueryDefaults implements ServiceDefaults<BigQuery, BigQueryOptions> {
@@ -191,8 +225,23 @@ public void setThrowNotFound(boolean setThrowNotFound) {
191225
this.setThrowNotFound = setThrowNotFound;
192226
}
193227

228+
/**
229+
* This setter is marked as Obsolete. Prefer {@link
230+
* Builder#setDataFormatOptions(DataFormatOptions)} to set the int64timestamp configuration
231+
* instead.
232+
*
233+
* <p>If useInt64Timestamps is set via DataFormatOptions, then the value in DataFormatOptions will
234+
* be used. Otherwise, this value will be passed to DataFormatOptions.
235+
*
236+
* <p>Alternative: {@code DataFormatOptions.newBuilder().setUseInt64Timestamp(...).build()}
237+
*/
238+
@ObsoleteApi("Use Builder#setDataFormatOptions(DataFormatOptions) instead")
194239
public void setUseInt64Timestamps(boolean useInt64Timestamps) {
195240
this.useInt64Timestamps = useInt64Timestamps;
241+
// Because this setter exists outside the Builder, DataFormatOptions needs be rebuilt to
242+
// account for this setting.
243+
this.dataFormatOptions =
244+
dataFormatOptions.toBuilder().useInt64Timestamp(useInt64Timestamps).build();
196245
}
197246

198247
@Deprecated
@@ -206,8 +255,22 @@ public boolean getThrowNotFound() {
206255
return setThrowNotFound;
207256
}
208257

258+
/**
259+
* This getter is marked as Obsolete. Prefer {@link
260+
* DataFormatOptions.Builder#useInt64Timestamp(boolean)} to set the int64timestamp configuration
261+
* instead.
262+
*
263+
* <p>Warning: DataFormatOptions values have precedence. Use {@link
264+
* DataFormatOptions#useInt64Timestamp()} to get `useInt64Timestamp` value used by the BigQuery
265+
* client.
266+
*/
267+
@ObsoleteApi("Use getDataFormatOptions().isUseInt64Timestamp() instead")
209268
public boolean getUseInt64Timestamps() {
210-
return useInt64Timestamps;
269+
return dataFormatOptions.useInt64Timestamp();
270+
}
271+
272+
public DataFormatOptions getDataFormatOptions() {
273+
return dataFormatOptions;
211274
}
212275

213276
public JobCreationMode getDefaultJobCreationMode() {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigquery;
17+
18+
import com.google.auto.value.AutoValue;
19+
import java.io.Serializable;
20+
21+
/**
22+
* Google BigQuery DataFormatOptions. Configures the output format for data types returned from
23+
* BigQuery.
24+
*/
25+
@AutoValue
26+
public abstract class DataFormatOptions implements Serializable {
27+
public enum TimestampFormatOptions {
28+
TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED("TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED"),
29+
FLOAT64("FLOAT64"),
30+
INT64("INT64"),
31+
ISO8601_STRING("ISO8601_STRING");
32+
33+
private final String format;
34+
35+
TimestampFormatOptions(String format) {
36+
this.format = format;
37+
}
38+
39+
@Override
40+
public String toString() {
41+
return format;
42+
}
43+
}
44+
45+
public abstract boolean useInt64Timestamp();
46+
47+
public abstract TimestampFormatOptions timestampFormatOptions();
48+
49+
public static Builder newBuilder() {
50+
return new AutoValue_DataFormatOptions.Builder()
51+
.useInt64Timestamp(false)
52+
.timestampFormatOptions(TimestampFormatOptions.TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED);
53+
}
54+
55+
public abstract Builder toBuilder();
56+
57+
@AutoValue.Builder
58+
public abstract static class Builder {
59+
public abstract Builder useInt64Timestamp(boolean useInt64Timestamp);
60+
61+
public abstract Builder timestampFormatOptions(TimestampFormatOptions timestampFormatOptions);
62+
63+
public abstract DataFormatOptions build();
64+
}
65+
66+
com.google.api.services.bigquery.model.DataFormatOptions toPb() {
67+
com.google.api.services.bigquery.model.DataFormatOptions request =
68+
new com.google.api.services.bigquery.model.DataFormatOptions();
69+
request.setUseInt64Timestamp(useInt64Timestamp());
70+
request.setTimestampOutputFormat(timestampFormatOptions().toString());
71+
return request;
72+
}
73+
}

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.api.services.bigquery.model.TableFieldSchema;
2626
import com.google.common.base.Function;
2727
import com.google.common.base.MoreObjects;
28+
import com.google.common.base.Preconditions;
2829
import com.google.common.collect.Lists;
2930
import java.io.Serializable;
3031
import java.util.List;
@@ -62,6 +63,7 @@ public TableFieldSchema apply(Field field) {
6263
private final Long maxLength;
6364
private final Long scale;
6465
private final Long precision;
66+
private final Long timestampPrecision;
6567
private final String defaultValueExpression;
6668
private final String collation;
6769
private final FieldElementType rangeElementType;
@@ -88,6 +90,7 @@ public static final class Builder {
8890
private Long maxLength;
8991
private Long scale;
9092
private Long precision;
93+
private Long timestampPrecision;
9194
private String defaultValueExpression;
9295
private String collation;
9396
private FieldElementType rangeElementType;
@@ -104,6 +107,7 @@ private Builder(Field field) {
104107
this.maxLength = field.maxLength;
105108
this.scale = field.scale;
106109
this.precision = field.precision;
110+
this.timestampPrecision = field.timestampPrecision;
107111
this.defaultValueExpression = field.defaultValueExpression;
108112
this.collation = field.collation;
109113
this.rangeElementType = field.rangeElementType;
@@ -254,6 +258,19 @@ public Builder setPrecision(Long precision) {
254258
return this;
255259
}
256260

261+
/**
262+
* Specifies the precision for TIMESTAMP types.
263+
*
264+
* <p>The default value is 6. Possible values are 6 (microsecond) or 12 (picosecond).
265+
*/
266+
public Builder setTimestampPrecision(Long timestampPrecision) {
267+
Preconditions.checkArgument(
268+
timestampPrecision == 6L || timestampPrecision == 12L,
269+
"Timestamp Precision must be 6 (microsecond) or 12 (picosecond)");
270+
this.timestampPrecision = timestampPrecision;
271+
return this;
272+
}
273+
257274
/**
258275
* DefaultValueExpression is used to specify the default value of a field using a SQL
259276
* expression. It can only be set for top level fields (columns).
@@ -317,6 +334,7 @@ private Field(Builder builder) {
317334
this.maxLength = builder.maxLength;
318335
this.scale = builder.scale;
319336
this.precision = builder.precision;
337+
this.timestampPrecision = builder.timestampPrecision;
320338
this.defaultValueExpression = builder.defaultValueExpression;
321339
this.collation = builder.collation;
322340
this.rangeElementType = builder.rangeElementType;
@@ -370,6 +388,11 @@ public Long getPrecision() {
370388
return precision;
371389
}
372390

391+
/** Returns the precision for TIMESTAMP type. */
392+
public Long getTimestampPrecision() {
393+
return timestampPrecision;
394+
}
395+
373396
/** Return the default value of the field. */
374397
public String getDefaultValueExpression() {
375398
return defaultValueExpression;
@@ -408,6 +431,7 @@ public String toString() {
408431
.add("maxLength", maxLength)
409432
.add("scale", scale)
410433
.add("precision", precision)
434+
.add("timestampPrecision", timestampPrecision)
411435
.add("defaultValueExpression", defaultValueExpression)
412436
.add("collation", collation)
413437
.add("rangeElementType", rangeElementType)
@@ -416,7 +440,19 @@ public String toString() {
416440

417441
@Override
418442
public int hashCode() {
419-
return Objects.hash(name, type, mode, description, policyTags, rangeElementType);
443+
return Objects.hash(
444+
name,
445+
type,
446+
mode,
447+
description,
448+
policyTags,
449+
maxLength,
450+
scale,
451+
precision,
452+
timestampPrecision,
453+
defaultValueExpression,
454+
collation,
455+
rangeElementType);
420456
}
421457

422458
@Override
@@ -490,6 +526,9 @@ TableFieldSchema toPb() {
490526
if (precision != null) {
491527
fieldSchemaPb.setPrecision(precision);
492528
}
529+
if (timestampPrecision != null) {
530+
fieldSchemaPb.setTimestampPrecision(timestampPrecision);
531+
}
493532
if (defaultValueExpression != null) {
494533
fieldSchemaPb.setDefaultValueExpression(defaultValueExpression);
495534
}
@@ -527,6 +566,9 @@ static Field fromPb(TableFieldSchema fieldSchemaPb) {
527566
if (fieldSchemaPb.getPrecision() != null) {
528567
fieldBuilder.setPrecision(fieldSchemaPb.getPrecision());
529568
}
569+
if (fieldSchemaPb.getTimestampPrecision() != null) {
570+
fieldBuilder.setTimestampPrecision(fieldSchemaPb.getTimestampPrecision());
571+
}
530572
if (fieldSchemaPb.getDefaultValueExpression() != null) {
531573
fieldBuilder.setDefaultValueExpression(fieldSchemaPb.getDefaultValueExpression());
532574
}

0 commit comments

Comments
 (0)