Skip to content

Commit d6c8295

Browse files
authored
Merge branch 'main' into default-checksum-json-read
2 parents 8efa1b9 + 1a6f4d5 commit d6c8295

16 files changed

Lines changed: 805 additions & 253 deletions

File tree

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,24 @@ public static DatasetListOption all() {
306306
}
307307
}
308308

309+
/** Class for specifying project list options. */
310+
@BetaApi
311+
class ProjectListOption extends Option {
312+
private static final long serialVersionUID = -7256063598324265038L;
313+
314+
private ProjectListOption(BigQueryRpc.Option option, Object value) {
315+
super(option, value);
316+
}
317+
318+
public static ProjectListOption pageSize(long pageSize) {
319+
return new ProjectListOption(BigQueryRpc.Option.MAX_RESULTS, pageSize);
320+
}
321+
322+
public static ProjectListOption pageToken(String pageToken) {
323+
return new ProjectListOption(BigQueryRpc.Option.PAGE_TOKEN, pageToken);
324+
}
325+
}
326+
309327
/** Class for specifying dataset get, create and update options. */
310328
class DatasetOption extends Option {
311329

@@ -951,6 +969,15 @@ public int hashCode() {
951969
*/
952970
Page<Dataset> listDatasets(DatasetListOption... options);
953971

972+
/**
973+
* Lists the projects accessible to the caller.
974+
*
975+
* @param options options for listing projects
976+
* @return a page of projects
977+
*/
978+
@BetaApi
979+
Page<Project> listProjects(ProjectListOption... options);
980+
954981
/**
955982
* Lists the datasets in the provided project. This method returns partial information on each
956983
* dataset: ({@link Dataset#getDatasetId()}, {@link Dataset#getFriendlyName()} and {@link

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

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.api.gax.paging.Page;
2626
import com.google.api.services.bigquery.model.ErrorProto;
2727
import com.google.api.services.bigquery.model.GetQueryResultsResponse;
28+
import com.google.api.services.bigquery.model.ProjectList;
2829
import com.google.api.services.bigquery.model.QueryRequest;
2930
import com.google.api.services.bigquery.model.TableDataInsertAllRequest;
3031
import com.google.api.services.bigquery.model.TableDataInsertAllRequest.Rows;
@@ -65,6 +66,25 @@
6566

6667
final class BigQueryImpl extends BaseService<BigQueryOptions> implements BigQuery {
6768

69+
private static class ProjectPageFetcher implements NextPageFetcher<Project> {
70+
71+
private static final long serialVersionUID = 1L;
72+
private final Map<BigQueryRpc.Option, ?> requestOptions;
73+
private final BigQueryOptions serviceOptions;
74+
75+
ProjectPageFetcher(
76+
BigQueryOptions serviceOptions, String cursor, Map<BigQueryRpc.Option, ?> optionMap) {
77+
this.requestOptions =
78+
PageImpl.nextRequestOptions(BigQueryRpc.Option.PAGE_TOKEN, cursor, optionMap);
79+
this.serviceOptions = serviceOptions;
80+
}
81+
82+
@Override
83+
public Page<Project> getNextPage() {
84+
return listProjects(serviceOptions, requestOptions);
85+
}
86+
}
87+
6888
private static class DatasetPageFetcher implements NextPageFetcher<Dataset> {
6989

7090
private static final long serialVersionUID = -3057564042439021278L;
@@ -307,6 +327,72 @@ public com.google.api.services.bigquery.model.Dataset call() throws IOException
307327
}
308328
}
309329

330+
@Override
331+
@BetaApi
332+
public Page<Project> listProjects(ProjectListOption... options) {
333+
Span projectsList = null;
334+
if (getOptions().isOpenTelemetryTracingEnabled()
335+
&& getOptions().getOpenTelemetryTracer() != null) {
336+
projectsList =
337+
getOptions()
338+
.getOpenTelemetryTracer()
339+
.spanBuilder("com.google.cloud.bigquery.BigQuery.listProjects")
340+
.setAllAttributes(otelAttributesFromOptions(options))
341+
.startSpan();
342+
}
343+
try (Scope projectsListScope = projectsList != null ? projectsList.makeCurrent() : null) {
344+
return listProjects(getOptions(), optionMap(options));
345+
} finally {
346+
if (projectsList != null) {
347+
projectsList.end();
348+
}
349+
}
350+
}
351+
352+
private static Page<Project> listProjects(
353+
final BigQueryOptions serviceOptions, final Map<BigQueryRpc.Option, ?> optionsMap) {
354+
try {
355+
Tuple<String, Iterable<ProjectList.Projects>> result =
356+
BigQueryRetryHelper.runWithRetries(
357+
new Callable<Tuple<String, Iterable<ProjectList.Projects>>>() {
358+
@Override
359+
public Tuple<String, Iterable<ProjectList.Projects>> call() {
360+
return serviceOptions.getBigQueryRpcV2().listProjects(optionsMap);
361+
}
362+
},
363+
serviceOptions.getRetrySettings(),
364+
serviceOptions.getResultRetryAlgorithm(),
365+
serviceOptions.getClock(),
366+
EMPTY_RETRY_CONFIG,
367+
serviceOptions.isOpenTelemetryTracingEnabled(),
368+
serviceOptions.getOpenTelemetryTracer());
369+
String nextPageToken = result.x();
370+
Iterable<Project> projects =
371+
Iterables.transform(
372+
result.y() != null ? result.y() : ImmutableList.<ProjectList.Projects>of(),
373+
new Function<ProjectList.Projects, Project>() {
374+
@Override
375+
public Project apply(ProjectList.Projects projectPb) {
376+
return new Project(
377+
projectPb.getId(),
378+
projectPb.getNumericId() != null
379+
? String.valueOf(projectPb.getNumericId())
380+
: null,
381+
projectPb.getProjectReference() != null
382+
? projectPb.getProjectReference().getProjectId()
383+
: null,
384+
projectPb.getFriendlyName());
385+
}
386+
});
387+
return new PageImpl<>(
388+
new ProjectPageFetcher(serviceOptions, nextPageToken, optionsMap),
389+
nextPageToken,
390+
projects);
391+
} catch (BigQueryRetryHelperException e) {
392+
throw BigQueryException.translateAndThrow(e);
393+
}
394+
}
395+
310396
@Override
311397
public Table create(TableInfo tableInfo, TableOption... options) {
312398
final com.google.api.services.bigquery.model.Table tablePb =
@@ -2083,11 +2169,15 @@ && getOptions().getOpenTelemetryTracer() != null) {
20832169
.startSpan();
20842170
}
20852171
try (Scope queryScope = querySpan != null ? querySpan.makeCurrent() : null) {
2086-
// If all parameters passed in configuration are supported by the query() method on the
2087-
// backend, put on fast path
2172+
// The fast query path (jobs.query API) is preferred to reduce latency by avoiding
2173+
// the slow fallback path (jobs.insert API). We will opt to use it if the configuration
2174+
// and JobId allow (i.e. if all parameters passed in configuration are supported).
20882175
QueryRequestInfo requestInfo =
20892176
new QueryRequestInfo(configuration, getOptions().getDataFormatOptions());
2090-
if (requestInfo.isFastQuerySupported(jobId)) {
2177+
// Fast query path is not possible if job is specified in the JobID object.
2178+
// Respect Job field value in JobId specified by user.
2179+
// Specifying it will force the query to take the slower path.
2180+
if (requestInfo.isFastQuerySupported() && (jobId == null || jobId.getJob() == null)) {
20912181
// Be careful when setting the projectID in JobId, if a projectID is specified in the JobId,
20922182
// the job created by the query method will use that project. This may cause the query to
20932183
// fail with "Access denied" if the project do not have enough permissions to run the job.

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,8 @@ private BigQueryResult getExecuteSelectResponse(
245245
labelMap = labels[0];
246246
}
247247
try {
248-
// use jobs.query if possible
248+
// The fast query path (jobs.query API) is preferred to reduce latency by avoiding
249+
// the slow fallback path (jobs.insert API). We will opt to use it if possible.
249250
if (isFastQuerySupported()) {
250251
logger.log(Level.INFO, "\n Using Fast Query Path");
251252
final String projectId = bigQueryOptions.getProjectId();
@@ -810,7 +811,8 @@ void flagEndOfStream() { // package-private
810811
Level.WARNING,
811812
"\n"
812813
+ Thread.currentThread().getName()
813-
+ " Could not flag End of Stream, both the buffer types are null. This might happen when the connection is close without executing a query");
814+
+ " Could not flag End of Stream, both the buffer types are null. This might happen"
815+
+ " when the connection is close without executing a query");
814816
}
815817
} catch (InterruptedException e) {
816818
logger.log(
@@ -1260,7 +1262,6 @@ boolean isFastQuerySupported() {
12601262
&& connectionSettings.getCreateDisposition() == null
12611263
&& connectionSettings.getDestinationEncryptionConfiguration() == null
12621264
&& connectionSettings.getDestinationTable() == null
1263-
&& connectionSettings.getJobTimeoutMs() == null
12641265
&& connectionSettings.getMaximumBillingTier() == null
12651266
&& connectionSettings.getPriority() == null
12661267
&& connectionSettings.getRangePartitioning() == null
@@ -1361,6 +1362,9 @@ QueryRequest createQueryRequest(
13611362
content.setRequestId(requestId);
13621363
// The new Connection interface only supports StandardSQL dialect
13631364
content.setUseLegacySql(false);
1365+
if (connectionSettings.getJobTimeoutMs() != null) {
1366+
content.setJobTimeoutMs(connectionSettings.getJobTimeoutMs());
1367+
}
13641368
return content;
13651369
}
13661370

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2026 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+
17+
package com.google.cloud.bigquery;
18+
19+
import com.google.api.core.BetaApi;
20+
import java.io.Serializable;
21+
import java.util.Objects;
22+
import javax.annotation.Nullable;
23+
24+
/**
25+
* Google BigQuery Project information. A project is the top-level container for Google Cloud
26+
* resources, and holds BigQuery dataset collections. This class wraps a BigQuery project resource,
27+
* providing details such as the project's unique alphanumeric ID, numeric project number, and
28+
* friendly display name.
29+
*
30+
* <p>Objects of this class can be obtained by listing projects accessible to the caller using
31+
* {@link BigQuery#listProjects(BigQuery.ProjectListOption...)}.
32+
*
33+
* @see <a href="https://cloud.google.com/bigquery/docs/reference/rest/v2/projects/list">Projects:
34+
* list</a>
35+
*/
36+
@BetaApi
37+
public class Project implements Serializable {
38+
private static final long serialVersionUID = -8123877292090683890L;
39+
40+
private final String id;
41+
private final String numericId;
42+
private final String projectId;
43+
private final String friendlyName;
44+
45+
public Project(String id, String numericId, String projectId, String friendlyName) {
46+
this.id = id;
47+
this.numericId = numericId;
48+
this.projectId = projectId;
49+
this.friendlyName = friendlyName;
50+
}
51+
52+
/** Returns the resource ID of the project. */
53+
public String getId() {
54+
return id;
55+
}
56+
57+
/** Returns the unique numeric project number. */
58+
@Nullable
59+
public String getNumericId() {
60+
return numericId;
61+
}
62+
63+
/** Returns the unique alphanumeric project ID. */
64+
@Nullable
65+
public String getProjectId() {
66+
return projectId;
67+
}
68+
69+
/** Returns the user-defined display name of the project. */
70+
@Nullable
71+
public String getFriendlyName() {
72+
return friendlyName;
73+
}
74+
75+
@Override
76+
public boolean equals(Object o) {
77+
if (this == o) return true;
78+
if (o == null || getClass() != o.getClass()) return false;
79+
Project project = (Project) o;
80+
return Objects.equals(id, project.id)
81+
&& Objects.equals(numericId, project.numericId)
82+
&& Objects.equals(projectId, project.projectId)
83+
&& Objects.equals(friendlyName, project.friendlyName);
84+
}
85+
86+
@Override
87+
public int hashCode() {
88+
return Objects.hash(id, numericId, projectId, friendlyName);
89+
}
90+
91+
@Override
92+
public String toString() {
93+
return "Project{"
94+
+ "id='"
95+
+ id
96+
+ '\''
97+
+ ", numericId='"
98+
+ numericId
99+
+ '\''
100+
+ ", projectId='"
101+
+ projectId
102+
+ '\''
103+
+ ", friendlyName='"
104+
+ friendlyName
105+
+ '\''
106+
+ '}';
107+
}
108+
}

0 commit comments

Comments
 (0)