Skip to content

Commit 113b8f2

Browse files
authored
feat: add support for preview features (#2923)
Enables preview query features, which currently only includes stateless queries (queries without jobId). These features won't always be enabled on the service side and there are additional checks and conditions. Fixes https://togithub.com/googleapis/java-bigquery/issues/2949
1 parent f452cf4 commit 113b8f2

File tree

8 files changed

+117
-2
lines changed

8 files changed

+117
-2
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,6 +1596,16 @@ TableResult listTableData(
15961596
* }
15971597
* </pre>
15981598
*
1599+
* This method supports query-related preview features via environmental variables (enabled by
1600+
* setting the {@code QUERY_PREVIEW_ENABLED} environment variable to "TRUE"). Specifically, this
1601+
* method supports:
1602+
*
1603+
* <ul>
1604+
* <li><b>Stateless queries</b>: query execution without corresponding job metadata
1605+
* </ul>
1606+
*
1607+
* The behaviour of these preview features is controlled by the bigquery service as well
1608+
*
15991609
* @throws BigQueryException upon failure
16001610
* @throws InterruptedException if the current thread gets interrupted while waiting for the query
16011611
* to complete

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.google.cloud.RetryHelper.RetryHelperException;
4242
import com.google.cloud.Tuple;
4343
import com.google.cloud.bigquery.InsertAllRequest.RowToInsert;
44+
import com.google.cloud.bigquery.QueryJobConfiguration.JobCreationMode;
4445
import com.google.cloud.bigquery.spi.v2.BigQueryRpc;
4546
import com.google.common.annotations.VisibleForTesting;
4647
import com.google.common.base.Function;
@@ -1324,6 +1325,14 @@ public TableResult query(QueryJobConfiguration configuration, JobOption... optio
13241325
throws InterruptedException, JobException {
13251326
Job.checkNotDryRun(configuration, "query");
13261327

1328+
if (getOptions().isQueryPreviewEnabled()) {
1329+
configuration =
1330+
configuration
1331+
.toBuilder()
1332+
.setJobCreationMode(JobCreationMode.JOB_CREATION_OPTIONAL)
1333+
.build();
1334+
}
1335+
13271336
// If all parameters passed in configuration are supported by the query() method on the backend,
13281337
// put on fast path
13291338
QueryRequestInfo requestInfo = new QueryRequestInfo(configuration);
@@ -1416,6 +1425,7 @@ public com.google.api.services.bigquery.model.QueryResponse call() {
14161425
public TableResult query(QueryJobConfiguration configuration, JobId jobId, JobOption... options)
14171426
throws InterruptedException, JobException {
14181427
Job.checkNotDryRun(configuration, "query");
1428+
14191429
// If all parameters passed in configuration are supported by the query() method on the backend,
14201430
// put on fast path
14211431
QueryRequestInfo requestInfo = new QueryRequestInfo(configuration);

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.cloud.bigquery.spi.v2.BigQueryRpc;
2525
import com.google.cloud.bigquery.spi.v2.HttpBigQueryRpc;
2626
import com.google.cloud.http.HttpTransportOptions;
27+
import com.google.common.annotations.VisibleForTesting;
2728
import com.google.common.collect.ImmutableSet;
2829
import java.util.Set;
2930

@@ -37,6 +38,7 @@ public class BigQueryOptions extends ServiceOptions<BigQuery, BigQueryOptions> {
3738
private final String location;
3839
// set the option ThrowNotFound when you want to throw the exception when the value not found
3940
private boolean setThrowNotFound;
41+
private String queryPreviewEnabled = System.getenv("QUERY_PREVIEW_ENABLED");
4042

4143
public static class DefaultBigQueryFactory implements BigQueryFactory {
4244

@@ -130,10 +132,19 @@ public String getLocation() {
130132
return location;
131133
}
132134

135+
public boolean isQueryPreviewEnabled() {
136+
return queryPreviewEnabled != null && queryPreviewEnabled.equalsIgnoreCase("TRUE");
137+
}
138+
133139
public void setThrowNotFound(boolean setThrowNotFound) {
134140
this.setThrowNotFound = setThrowNotFound;
135141
}
136142

143+
@VisibleForTesting
144+
public void setQueryPreviewEnabled(String queryPreviewEnabled) {
145+
this.queryPreviewEnabled = queryPreviewEnabled;
146+
}
147+
137148
public boolean getThrowNotFound() {
138149
return setThrowNotFound;
139150
}

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public final class QueryJobConfiguration extends JobConfiguration {
7373
private final List<ConnectionProperty> connectionProperties;
7474
// maxResults is only used for fast query path
7575
private final Long maxResults;
76+
private final JobCreationMode jobCreationMode;
7677

7778
/**
7879
* Priority levels for a query. If not specified the priority is assumed to be {@link
@@ -94,6 +95,21 @@ public enum Priority {
9495
BATCH
9596
}
9697

98+
/** Job Creation Mode provides different options on job creation. */
99+
enum JobCreationMode {
100+
/** Unspecified JobCreationMode, defaults to JOB_CREATION_REQUIRED. */
101+
JOB_CREATION_MODE_UNSPECIFIED,
102+
/** Default. Job creation is always required. */
103+
JOB_CREATION_REQUIRED,
104+
/**
105+
* Job creation is optional. Returning immediate results is prioritized. BigQuery will
106+
* automatically determine if a Job needs to be created. The conditions under which BigQuery can
107+
* decide to not create a Job are subject to change. If Job creation is required,
108+
* JOB_CREATION_REQUIRED mode should be used, which is the default.
109+
*/
110+
JOB_CREATION_OPTIONAL,
111+
}
112+
97113
public static final class Builder
98114
extends JobConfiguration.Builder<QueryJobConfiguration, Builder> {
99115

@@ -125,6 +141,7 @@ public static final class Builder
125141
private RangePartitioning rangePartitioning;
126142
private List<ConnectionProperty> connectionProperties;
127143
private Long maxResults;
144+
private JobCreationMode jobCreationMode;
128145

129146
private Builder() {
130147
super(Type.QUERY);
@@ -160,6 +177,7 @@ private Builder(QueryJobConfiguration jobConfiguration) {
160177
this.rangePartitioning = jobConfiguration.rangePartitioning;
161178
this.connectionProperties = jobConfiguration.connectionProperties;
162179
this.maxResults = jobConfiguration.maxResults;
180+
this.jobCreationMode = jobConfiguration.jobCreationMode;
163181
}
164182

165183
private Builder(com.google.api.services.bigquery.model.JobConfiguration configurationPb) {
@@ -655,6 +673,15 @@ public Builder setMaxResults(Long maxResults) {
655673
return this;
656674
}
657675

676+
/**
677+
* Provides different options on job creation. If not specified the job creation mode is assumed
678+
* to be {@link JobCreationMode#JOB_CREATION_REQUIRED}.
679+
*/
680+
Builder setJobCreationMode(JobCreationMode jobCreationMode) {
681+
this.jobCreationMode = jobCreationMode;
682+
return this;
683+
}
684+
658685
public QueryJobConfiguration build() {
659686
return new QueryJobConfiguration(this);
660687
}
@@ -699,6 +726,7 @@ private QueryJobConfiguration(Builder builder) {
699726
this.rangePartitioning = builder.rangePartitioning;
700727
this.connectionProperties = builder.connectionProperties;
701728
this.maxResults = builder.maxResults;
729+
this.jobCreationMode = builder.jobCreationMode;
702730
}
703731

704732
/**
@@ -910,6 +938,11 @@ public Long getMaxResults() {
910938
return maxResults;
911939
}
912940

941+
/** Returns the job creation mode. */
942+
JobCreationMode getJobCreationMode() {
943+
return jobCreationMode;
944+
}
945+
913946
@Override
914947
public Builder toBuilder() {
915948
return new Builder(this);
@@ -944,7 +977,8 @@ ToStringHelper toStringHelper() {
944977
.add("jobTimeoutMs", jobTimeoutMs)
945978
.add("labels", labels)
946979
.add("rangePartitioning", rangePartitioning)
947-
.add("connectionProperties", connectionProperties);
980+
.add("connectionProperties", connectionProperties)
981+
.add("jobCreationMode", jobCreationMode);
948982
}
949983

950984
@Override

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.api.services.bigquery.model.QueryParameter;
2020
import com.google.api.services.bigquery.model.QueryRequest;
21+
import com.google.cloud.bigquery.QueryJobConfiguration.JobCreationMode;
2122
import com.google.common.base.MoreObjects;
2223
import com.google.common.base.Objects;
2324
import com.google.common.collect.Lists;
@@ -40,6 +41,7 @@ final class QueryRequestInfo {
4041
private final Boolean createSession;
4142
private final Boolean useQueryCache;
4243
private final Boolean useLegacySql;
44+
private final JobCreationMode jobCreationMode;
4345

4446
QueryRequestInfo(QueryJobConfiguration config) {
4547
this.config = config;
@@ -55,6 +57,7 @@ final class QueryRequestInfo {
5557
this.createSession = config.createSession();
5658
this.useLegacySql = config.useLegacySql();
5759
this.useQueryCache = config.useQueryCache();
60+
this.jobCreationMode = config.getJobCreationMode();
5861
}
5962

6063
boolean isFastQuerySupported(JobId jobId) {
@@ -116,6 +119,9 @@ QueryRequest toPb() {
116119
if (useQueryCache != null) {
117120
request.setUseQueryCache(useQueryCache);
118121
}
122+
if (jobCreationMode != null) {
123+
request.setJobCreationMode(jobCreationMode.toString());
124+
}
119125
return request;
120126
}
121127

@@ -134,6 +140,7 @@ public String toString() {
134140
.add("createSession", createSession)
135141
.add("useQueryCache", useQueryCache)
136142
.add("useLegacySql", useLegacySql)
143+
.add("jobCreationMode", jobCreationMode)
137144
.toString();
138145
}
139146

@@ -151,7 +158,8 @@ public int hashCode() {
151158
requestId,
152159
createSession,
153160
useQueryCache,
154-
useLegacySql);
161+
useLegacySql,
162+
jobCreationMode);
155163
}
156164

157165
@Override

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.cloud.bigquery.JobInfo.CreateDisposition;
2424
import com.google.cloud.bigquery.JobInfo.SchemaUpdateOption;
2525
import com.google.cloud.bigquery.JobInfo.WriteDisposition;
26+
import com.google.cloud.bigquery.QueryJobConfiguration.JobCreationMode;
2627
import com.google.cloud.bigquery.QueryJobConfiguration.Priority;
2728
import com.google.cloud.bigquery.TimePartitioning.Type;
2829
import com.google.common.collect.ImmutableList;
@@ -110,6 +111,7 @@ public class QueryJobConfigurationTest {
110111
private static final Map<String, QueryParameterValue> NAME_PARAMETER =
111112
ImmutableMap.of("string", STRING_PARAMETER, "timestamp", TIMESTAMP_PARAMETER);
112113
private static final String PARAMETER_MODE = "POSITIONAL";
114+
private static final JobCreationMode JOB_CREATION_MODE = JobCreationMode.JOB_CREATION_OPTIONAL;
113115
private static final QueryJobConfiguration QUERY_JOB_CONFIGURATION =
114116
QueryJobConfiguration.newBuilder(QUERY)
115117
.setUseQueryCache(USE_QUERY_CACHE)
@@ -150,6 +152,8 @@ public class QueryJobConfigurationTest {
150152
.setPositionalParameters(ImmutableList.<QueryParameterValue>of())
151153
.setNamedParameters(NAME_PARAMETER)
152154
.build();
155+
private static final QueryJobConfiguration QUERY_JOB_CONFIGURATION_SET_JOB_CREATION_MODE =
156+
QUERY_JOB_CONFIGURATION.toBuilder().setJobCreationMode(JOB_CREATION_MODE).build();
153157

154158
@Test
155159
public void testToBuilder() {
@@ -230,6 +234,13 @@ public void testNamedParameter() {
230234
QUERY_JOB_CONFIGURATION_SET_NAME_PARAMETER.toBuilder().build());
231235
}
232236

237+
@Test
238+
public void testJobCreationMode() {
239+
compareQueryJobConfiguration(
240+
QUERY_JOB_CONFIGURATION_SET_JOB_CREATION_MODE,
241+
QUERY_JOB_CONFIGURATION_SET_JOB_CREATION_MODE.toBuilder().build());
242+
}
243+
233244
private void compareQueryJobConfiguration(
234245
QueryJobConfiguration expected, QueryJobConfiguration value) {
235246
assertEquals(expected, value);

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.cloud.bigquery.JobInfo.CreateDisposition;
2424
import com.google.cloud.bigquery.JobInfo.SchemaUpdateOption;
2525
import com.google.cloud.bigquery.JobInfo.WriteDisposition;
26+
import com.google.cloud.bigquery.QueryJobConfiguration.JobCreationMode;
2627
import com.google.cloud.bigquery.QueryJobConfiguration.Priority;
2728
import com.google.common.collect.ImmutableList;
2829
import com.google.common.collect.ImmutableMap;
@@ -105,6 +106,8 @@ public class QueryRequestInfoTest {
105106
ImmutableList.of(STRING_PARAMETER, TIMESTAMP_PARAMETER);
106107
private static final Map<String, QueryParameterValue> NAME_PARAMETER =
107108
ImmutableMap.of("string", STRING_PARAMETER, "timestamp", TIMESTAMP_PARAMETER);
109+
private static final JobCreationMode jobCreationModeRequired =
110+
JobCreationMode.JOB_CREATION_REQUIRED;
108111
private static final QueryJobConfiguration QUERY_JOB_CONFIGURATION =
109112
QueryJobConfiguration.newBuilder(QUERY)
110113
.setUseQueryCache(USE_QUERY_CACHE)
@@ -131,6 +134,7 @@ public class QueryRequestInfoTest {
131134
.setConnectionProperties(CONNECTION_PROPERTIES)
132135
.setPositionalParameters(POSITIONAL_PARAMETER)
133136
.setMaxResults(100L)
137+
.setJobCreationMode(jobCreationModeRequired)
134138
.build();
135139
QueryRequestInfo REQUEST_INFO = new QueryRequestInfo(QUERY_JOB_CONFIGURATION);
136140
private static final QueryJobConfiguration QUERY_JOB_CONFIGURATION_SUPPORTED =
@@ -194,5 +198,6 @@ private void compareQueryRequestInfo(QueryRequestInfo expected, QueryRequestInfo
194198
assertEquals(expectedQueryReq.getCreateSession(), actualQueryReq.getCreateSession());
195199
assertEquals(expectedQueryReq.getUseQueryCache(), actualQueryReq.getUseQueryCache());
196200
assertEquals(expectedQueryReq.getUseLegacySql(), actualQueryReq.getUseLegacySql());
201+
assertEquals(expectedQueryReq.get("jobCreationMode"), actualQueryReq.get("jobCreationMode"));
197202
}
198203
}

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6188,4 +6188,30 @@ public void testAlreadyExistJobExceptionHandling() throws InterruptedException {
61886188
}
61896189
}
61906190
}
6191+
6192+
@Test
6193+
public void testStatelessQueries() throws InterruptedException {
6194+
// simulate setting the QUERY_PREVIEW_ENABLED environment variable
6195+
bigquery.getOptions().setQueryPreviewEnabled("TRUE");
6196+
assertNull(executeSimpleQuery().getJobId());
6197+
6198+
// the flag should be case-insensitive
6199+
bigquery.getOptions().setQueryPreviewEnabled("tRuE");
6200+
assertNull(executeSimpleQuery().getJobId());
6201+
6202+
// any other values won't enable optional job creation mode
6203+
bigquery.getOptions().setQueryPreviewEnabled("test_value");
6204+
assertNotNull(executeSimpleQuery().getJobId());
6205+
6206+
// reset the flag
6207+
bigquery.getOptions().setQueryPreviewEnabled(null);
6208+
assertNotNull(executeSimpleQuery().getJobId());
6209+
}
6210+
6211+
private TableResult executeSimpleQuery() throws InterruptedException {
6212+
String query = "SELECT 1 as one";
6213+
QueryJobConfiguration config = QueryJobConfiguration.newBuilder(query).build();
6214+
TableResult result = bigquery.query(config);
6215+
return result;
6216+
}
61916217
}

0 commit comments

Comments
 (0)