From 6003cedc009ea6e4da4e53ee359df76f5e5d8332 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Thu, 20 Aug 2026 14:40:49 +0000 Subject: [PATCH 1/4] feat(bigquery): expose StatementType and query execution stats on TableResult --- .../google/cloud/bigquery/BigQueryImpl.java | 20 ++++ .../java/com/google/cloud/bigquery/Job.java | 27 ++++- .../google/cloud/bigquery/TableResult.java | 65 ++++++++++- .../cloud/bigquery/BigQueryImplTest.java | 11 ++ .../cloud/bigquery/TableResultTest.java | 106 ++++++++++++++++++ java-bigquery/pom.xml | 2 +- 6 files changed, 227 insertions(+), 4 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java index 2ad09c33d7cb..40d78bb60c6d 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java @@ -41,6 +41,7 @@ import com.google.cloud.Tuple; import com.google.cloud.bigquery.BigQueryRetryHelper.BigQueryRetryHelperException; import com.google.cloud.bigquery.InsertAllRequest.RowToInsert; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics.StatementType; import com.google.cloud.bigquery.spi.v2.BigQueryRpc; import com.google.cloud.bigquery.spi.v2.HttpBigQueryRpc; import com.google.common.annotations.VisibleForTesting; @@ -2095,6 +2096,15 @@ public com.google.api.services.bigquery.model.QueryResponse call() return job; } + StatementType statementType = + results.getStatementType() != null + ? StatementType.valueOf(results.getStatementType()) + : null; + Long totalBytesBilled = results.getTotalBytesBilled(); + Long totalBytesProcessed = results.getTotalBytesProcessed(); + Long totalSlotMs = results.getTotalSlotMs(); + Long numDmlAffectedRows = results.getNumDmlAffectedRows(); + if (results.getPageToken() != null) { JobId jobId = JobId.fromPb(results.getJobReference()); String cursor = results.getPageToken(); @@ -2114,6 +2124,11 @@ public com.google.api.services.bigquery.model.QueryResponse call() .setQueryId(results.getQueryId()) .setJobCreationReason(JobCreationReason.fromPb(results.getJobCreationReason())) .setRowsInPage(results.getRows() != null ? (long) results.getRows().size() : 0L) + .setStatementType(statementType) + .setTotalBytesBilled(totalBytesBilled) + .setTotalBytesProcessed(totalBytesProcessed) + .setTotalSlotMs(totalSlotMs) + .setNumDmlAffectedRows(numDmlAffectedRows) .build(); } // only 1 page of result @@ -2134,6 +2149,11 @@ public com.google.api.services.bigquery.model.QueryResponse call() .setQueryId(results.getQueryId()) .setJobCreationReason(JobCreationReason.fromPb(results.getJobCreationReason())) .setRowsInPage(results.getRows() != null ? (long) results.getRows().size() : 0L) + .setStatementType(statementType) + .setTotalBytesBilled(totalBytesBilled) + .setTotalBytesProcessed(totalBytesProcessed) + .setTotalSlotMs(totalSlotMs) + .setNumDmlAffectedRows(numDmlAffectedRows) .build(); } diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java index 43832f3410b8..614a436a5744 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java @@ -28,6 +28,8 @@ import com.google.cloud.bigquery.BigQuery.QueryResultsOption; import com.google.cloud.bigquery.BigQuery.TableDataListOption; import com.google.cloud.bigquery.JobConfiguration.Type; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics.StatementType; import com.google.common.collect.ImmutableList; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Span; @@ -414,6 +416,16 @@ public TableResult getQueryResults(QueryResultsOption... options) : ImmutableList.copyOf(job.getStatus().getExecutionErrors())); } + QueryStatistics stats = + job.getStatistics() instanceof QueryStatistics + ? (QueryStatistics) job.getStatistics() + : null; + StatementType statementType = stats != null ? stats.getStatementType() : null; + Long totalBytesBilled = stats != null ? stats.getTotalBytesBilled() : null; + Long totalBytesProcessed = stats != null ? stats.getTotalBytesProcessed() : null; + Long totalSlotMs = stats != null ? stats.getTotalSlotMs() : null; + Long numDmlAffectedRows = stats != null ? stats.getNumDmlAffectedRows() : null; + // If there are no rows in the result, this may have been a DDL query. // Listing table data might fail, such as with CREATE VIEW queries. // Avoid a tabledata.list API request by returning an empty TableResult. @@ -425,6 +437,11 @@ public TableResult getQueryResults(QueryResultsOption... options) .setTotalRows(0L) .setPageNoSchema(new PageImpl(null, "", null)) .setRowsInPage(0L) + .setStatementType(statementType) + .setTotalBytesBilled(totalBytesBilled) + .setTotalBytesProcessed(totalBytesProcessed) + .setTotalSlotMs(totalSlotMs) + .setNumDmlAffectedRows(numDmlAffectedRows) .build(); return emptyTableResult; } @@ -436,7 +453,15 @@ public TableResult getQueryResults(QueryResultsOption... options) TableResult tableResult = bigquery.listTableData( table, response.getSchema(), listOptions.toArray(new TableDataListOption[0])); - TableResult tableResultWithJobId = tableResult.toBuilder().setJobId(job.getJobId()).build(); + TableResult tableResultWithJobId = + tableResult.toBuilder() + .setJobId(job.getJobId()) + .setStatementType(statementType) + .setTotalBytesBilled(totalBytesBilled) + .setTotalBytesProcessed(totalBytesProcessed) + .setTotalSlotMs(totalSlotMs) + .setNumDmlAffectedRows(numDmlAffectedRows) + .build(); return tableResultWithJobId; } finally { if (getQueryResults != null) { diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java index a791628c5b16..df62176af99f 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java @@ -18,6 +18,7 @@ import com.google.api.gax.paging.Page; import com.google.auto.value.AutoValue; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics.StatementType; import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.collect.Iterables; @@ -51,6 +52,16 @@ public abstract static class Builder { abstract TableResult.Builder setRowsInPage(Long rowsInPage); + public abstract TableResult.Builder setStatementType(StatementType statementType); + + public abstract TableResult.Builder setTotalBytesBilled(Long totalBytesBilled); + + public abstract TableResult.Builder setTotalBytesProcessed(Long totalBytesProcessed); + + public abstract TableResult.Builder setTotalSlotMs(Long totalSlotMs); + + public abstract TableResult.Builder setNumDmlAffectedRows(Long numDmlAffectedRows); + /** Creates a @code TableResult} object. */ public abstract TableResult build(); } @@ -87,6 +98,32 @@ public static Builder newBuilder() { @Nullable public abstract Long getRowsInPage(); + /** + * Returns the statement type of the query (e.g. SELECT, INSERT, UPDATE, DDL, SCRIPT), if + * available. + */ + @Nullable + public abstract StatementType getStatementType(); + + /** Returns the total number of bytes billed for the query, if available. */ + @Nullable + public abstract Long getTotalBytesBilled(); + + /** Returns the total number of bytes processed by the query, if available. */ + @Nullable + public abstract Long getTotalBytesProcessed(); + + /** Returns the total slot milliseconds for the query, if available. */ + @Nullable + public abstract Long getTotalSlotMs(); + + /** + * Returns the number of rows affected by a DML statement (INSERT, UPDATE, DELETE, MERGE), if + * available. + */ + @Nullable + public abstract Long getNumDmlAffectedRows(); + @Override public boolean hasNextPage() { return getPageNoSchema().hasNextPage(); @@ -109,6 +146,11 @@ public TableResult getNextPage() { .setQueryId(getQueryId()) .setJobCreationReason(getJobCreationReason()) .setRowsInPage(nextRows) + .setStatementType(getStatementType()) + .setTotalBytesBilled(getTotalBytesBilled()) + .setTotalBytesProcessed(getTotalBytesProcessed()) + .setTotalSlotMs(getTotalSlotMs()) + .setNumDmlAffectedRows(getNumDmlAffectedRows()) .build(); } return null; @@ -147,13 +189,27 @@ public String toString() { .add("cursor", getNextPageToken()) .add("queryId", getQueryId()) .add("rowsInPage", getRowsInPage()) + .add("statementType", getStatementType()) + .add("totalBytesBilled", getTotalBytesBilled()) + .add("totalBytesProcessed", getTotalBytesProcessed()) + .add("totalSlotMs", getTotalSlotMs()) + .add("numDmlAffectedRows", getNumDmlAffectedRows()) .toString(); } @Override public final int hashCode() { return Objects.hash( - getPageNoSchema(), getSchema(), getTotalRows(), getQueryId(), getRowsInPage()); + getPageNoSchema(), + getSchema(), + getTotalRows(), + getQueryId(), + getRowsInPage(), + getStatementType(), + getTotalBytesBilled(), + getTotalBytesProcessed(), + getTotalSlotMs(), + getNumDmlAffectedRows()); } @Override @@ -170,6 +226,11 @@ public final boolean equals(Object obj) { && Objects.equals(getSchema(), response.getSchema()) && getTotalRows() == response.getTotalRows() && Objects.equals(getQueryId(), response.getQueryId()) - && Objects.equals(getRowsInPage(), response.getRowsInPage()); + && Objects.equals(getRowsInPage(), response.getRowsInPage()) + && Objects.equals(getStatementType(), response.getStatementType()) + && Objects.equals(getTotalBytesBilled(), response.getTotalBytesBilled()) + && Objects.equals(getTotalBytesProcessed(), response.getTotalBytesProcessed()) + && Objects.equals(getTotalSlotMs(), response.getTotalSlotMs()) + && Objects.equals(getNumDmlAffectedRows(), response.getNumDmlAffectedRows()); } } diff --git a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java index 9f2320ab3c35..434d317c420f 100644 --- a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java +++ b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java @@ -65,6 +65,7 @@ import com.google.cloud.bigquery.BigQuery.JobOption; import com.google.cloud.bigquery.BigQuery.QueryResultsOption; import com.google.cloud.bigquery.InsertAllRequest.RowToInsert; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics.StatementType; import com.google.cloud.bigquery.spi.BigQueryRpcFactory; import com.google.cloud.bigquery.spi.v2.BigQueryRpc; import com.google.cloud.bigquery.spi.v2.HttpBigQueryRpc; @@ -2874,7 +2875,11 @@ void testQueryWithTimeoutSetsTimeout() throws InterruptedException, IOException .setPageToken(null) .setRows(ImmutableList.of(TABLE_ROW)) .setSchema(TABLE_SCHEMA.toPb()) + .setStatementType("SELECT") + .setTotalBytesBilled(100L) .setTotalBytesProcessed(42L) + .setTotalSlotMs(50L) + .setNumDmlAffectedRows(0L) .setTotalRows(BigInteger.valueOf(1L)); when(bigqueryRpcMock.queryRpcSkipExceptionTranslation(eq(PROJECT), requestPbCapture.capture())) @@ -2883,6 +2888,12 @@ void testQueryWithTimeoutSetsTimeout() throws InterruptedException, IOException bigquery = options.getService(); Object result = bigquery.queryWithTimeout(QUERY_JOB_CONFIGURATION_FOR_QUERY, null, 1000L); assertTrue(result instanceof TableResult); + TableResult tableResult = (TableResult) result; + assertEquals(StatementType.SELECT, tableResult.getStatementType()); + assertEquals((Long) 100L, tableResult.getTotalBytesBilled()); + assertEquals((Long) 42L, tableResult.getTotalBytesProcessed()); + assertEquals((Long) 50L, tableResult.getTotalSlotMs()); + assertEquals((Long) 0L, tableResult.getNumDmlAffectedRows()); QueryRequest requestPb = requestPbCapture.getValue(); assertEquals((Long) 1000L, requestPb.getTimeoutMs()); } diff --git a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/TableResultTest.java b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/TableResultTest.java index 90ae2692f00c..fe9d33d519ec 100644 --- a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/TableResultTest.java +++ b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/TableResultTest.java @@ -115,4 +115,110 @@ void testSchema() { newFieldValueList("2").withSchema(SCHEMA.getFields())) .inOrder(); } + + @Test + void testStatementTypeAndExecutionStats() { + TableResult result = + TableResult.newBuilder() + .setSchema(SCHEMA) + .setTotalRows(3L) + .setPageNoSchema(INNER_PAGE_0) + .setRowsInPage(2L) + .setStatementType(JobStatistics.QueryStatistics.StatementType.SELECT) + .setTotalBytesBilled(1024L) + .setTotalBytesProcessed(2048L) + .setTotalSlotMs(500L) + .setNumDmlAffectedRows(0L) + .build(); + + assertThat(result.getStatementType()) + .isEqualTo(JobStatistics.QueryStatistics.StatementType.SELECT); + assertThat(result.getTotalBytesBilled()).isEqualTo(1024L); + assertThat(result.getTotalBytesProcessed()).isEqualTo(2048L); + assertThat(result.getTotalSlotMs()).isEqualTo(500L); + assertThat(result.getNumDmlAffectedRows()).isEqualTo(0L); + + TableResult next = result.getNextPage(); + assertThat(next.getStatementType()) + .isEqualTo(JobStatistics.QueryStatistics.StatementType.SELECT); + assertThat(next.getTotalBytesBilled()).isEqualTo(1024L); + assertThat(next.getTotalBytesProcessed()).isEqualTo(2048L); + assertThat(next.getTotalSlotMs()).isEqualTo(500L); + assertThat(next.getNumDmlAffectedRows()).isEqualTo(0L); + } + + @Test + void testToBuilder() { + TableResult result = + TableResult.newBuilder() + .setSchema(SCHEMA) + .setTotalRows(3L) + .setPageNoSchema(INNER_PAGE_0) + .setRowsInPage(2L) + .setStatementType(JobStatistics.QueryStatistics.StatementType.INSERT) + .setTotalBytesBilled(500L) + .setTotalBytesProcessed(1000L) + .setTotalSlotMs(250L) + .setNumDmlAffectedRows(5L) + .build(); + + TableResult modified = + result.toBuilder() + .setStatementType(JobStatistics.QueryStatistics.StatementType.UPDATE) + .setNumDmlAffectedRows(10L) + .build(); + + assertThat(modified.getStatementType()) + .isEqualTo(JobStatistics.QueryStatistics.StatementType.UPDATE); + assertThat(modified.getNumDmlAffectedRows()).isEqualTo(10L); + assertThat(modified.getTotalBytesBilled()).isEqualTo(500L); + } + + @Test + void testEqualsAndHashCode() { + TableResult result1 = + TableResult.newBuilder() + .setSchema(SCHEMA) + .setTotalRows(3L) + .setPageNoSchema(INNER_PAGE_0) + .setRowsInPage(2L) + .setStatementType(JobStatistics.QueryStatistics.StatementType.SELECT) + .setTotalBytesBilled(100L) + .setTotalBytesProcessed(200L) + .setTotalSlotMs(50L) + .setNumDmlAffectedRows(0L) + .build(); + + TableResult result2 = + TableResult.newBuilder() + .setSchema(SCHEMA) + .setTotalRows(3L) + .setPageNoSchema(INNER_PAGE_0) + .setRowsInPage(2L) + .setStatementType(JobStatistics.QueryStatistics.StatementType.SELECT) + .setTotalBytesBilled(100L) + .setTotalBytesProcessed(200L) + .setTotalSlotMs(50L) + .setNumDmlAffectedRows(0L) + .build(); + + TableResult result3 = + TableResult.newBuilder() + .setSchema(SCHEMA) + .setTotalRows(3L) + .setPageNoSchema(INNER_PAGE_0) + .setRowsInPage(2L) + .setStatementType(JobStatistics.QueryStatistics.StatementType.DELETE) + .setTotalBytesBilled(100L) + .setTotalBytesProcessed(200L) + .setTotalSlotMs(50L) + .setNumDmlAffectedRows(1L) + .build(); + + assertThat(result1).isEqualTo(result2); + assertThat(result1.hashCode()).isEqualTo(result2.hashCode()); + assertThat(result1).isNotEqualTo(result3); + assertThat(result1.toString()).contains("statementType=SELECT"); + assertThat(result1.toString()).contains("totalBytesBilled=100"); + } } diff --git a/java-bigquery/pom.xml b/java-bigquery/pom.xml index ee1fcd22dfc8..b480390dd2e7 100644 --- a/java-bigquery/pom.xml +++ b/java-bigquery/pom.xml @@ -55,7 +55,7 @@ UTF-8 github google-cloud-bigquery-parent - v2-rev20251012-2.0.0 + v2-rev20260731-2.0.0 From d4f4546b2127f7dc88b9c5ad0d42377bae481a96 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Fri, 21 Aug 2026 17:17:34 +0000 Subject: [PATCH 2/4] feat(bigquery): expose SessionInfo on TableResult --- .../google/cloud/bigquery/BigQueryImpl.java | 5 +++ .../java/com/google/cloud/bigquery/Job.java | 4 ++ .../google/cloud/bigquery/TableResult.java | 15 ++++++- .../cloud/bigquery/BigQueryImplTest.java | 6 +++ .../cloud/bigquery/TableResultTest.java | 42 +++++++++++++------ 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java index 40d78bb60c6d..da4b11e676dd 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java @@ -42,6 +42,7 @@ import com.google.cloud.bigquery.BigQueryRetryHelper.BigQueryRetryHelperException; import com.google.cloud.bigquery.InsertAllRequest.RowToInsert; import com.google.cloud.bigquery.JobStatistics.QueryStatistics.StatementType; +import com.google.cloud.bigquery.JobStatistics.SessionInfo; import com.google.cloud.bigquery.spi.v2.BigQueryRpc; import com.google.cloud.bigquery.spi.v2.HttpBigQueryRpc; import com.google.common.annotations.VisibleForTesting; @@ -2104,6 +2105,8 @@ public com.google.api.services.bigquery.model.QueryResponse call() Long totalBytesProcessed = results.getTotalBytesProcessed(); Long totalSlotMs = results.getTotalSlotMs(); Long numDmlAffectedRows = results.getNumDmlAffectedRows(); + SessionInfo sessionInfo = + results.getSessionInfo() != null ? SessionInfo.fromPb(results.getSessionInfo()) : null; if (results.getPageToken() != null) { JobId jobId = JobId.fromPb(results.getJobReference()); @@ -2129,6 +2132,7 @@ public com.google.api.services.bigquery.model.QueryResponse call() .setTotalBytesProcessed(totalBytesProcessed) .setTotalSlotMs(totalSlotMs) .setNumDmlAffectedRows(numDmlAffectedRows) + .setSessionInfo(sessionInfo) .build(); } // only 1 page of result @@ -2154,6 +2158,7 @@ public com.google.api.services.bigquery.model.QueryResponse call() .setTotalBytesProcessed(totalBytesProcessed) .setTotalSlotMs(totalSlotMs) .setNumDmlAffectedRows(numDmlAffectedRows) + .setSessionInfo(sessionInfo) .build(); } diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java index 614a436a5744..ca68b87db97c 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Job.java @@ -30,6 +30,7 @@ import com.google.cloud.bigquery.JobConfiguration.Type; import com.google.cloud.bigquery.JobStatistics.QueryStatistics; import com.google.cloud.bigquery.JobStatistics.QueryStatistics.StatementType; +import com.google.cloud.bigquery.JobStatistics.SessionInfo; import com.google.common.collect.ImmutableList; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Span; @@ -425,6 +426,7 @@ public TableResult getQueryResults(QueryResultsOption... options) Long totalBytesProcessed = stats != null ? stats.getTotalBytesProcessed() : null; Long totalSlotMs = stats != null ? stats.getTotalSlotMs() : null; Long numDmlAffectedRows = stats != null ? stats.getNumDmlAffectedRows() : null; + SessionInfo sessionInfo = stats != null ? stats.getSessionInfo() : null; // If there are no rows in the result, this may have been a DDL query. // Listing table data might fail, such as with CREATE VIEW queries. @@ -442,6 +444,7 @@ public TableResult getQueryResults(QueryResultsOption... options) .setTotalBytesProcessed(totalBytesProcessed) .setTotalSlotMs(totalSlotMs) .setNumDmlAffectedRows(numDmlAffectedRows) + .setSessionInfo(sessionInfo) .build(); return emptyTableResult; } @@ -461,6 +464,7 @@ public TableResult getQueryResults(QueryResultsOption... options) .setTotalBytesProcessed(totalBytesProcessed) .setTotalSlotMs(totalSlotMs) .setNumDmlAffectedRows(numDmlAffectedRows) + .setSessionInfo(sessionInfo) .build(); return tableResultWithJobId; } finally { diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java index df62176af99f..66ac8678bf19 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java @@ -19,6 +19,7 @@ import com.google.api.gax.paging.Page; import com.google.auto.value.AutoValue; import com.google.cloud.bigquery.JobStatistics.QueryStatistics.StatementType; +import com.google.cloud.bigquery.JobStatistics.SessionInfo; import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.collect.Iterables; @@ -62,6 +63,8 @@ public abstract static class Builder { public abstract TableResult.Builder setNumDmlAffectedRows(Long numDmlAffectedRows); + public abstract TableResult.Builder setSessionInfo(SessionInfo sessionInfo); + /** Creates a @code TableResult} object. */ public abstract TableResult build(); } @@ -124,6 +127,10 @@ public static Builder newBuilder() { @Nullable public abstract Long getNumDmlAffectedRows(); + /** Returns information about the session if this query is part of one, if available. */ + @Nullable + public abstract SessionInfo getSessionInfo(); + @Override public boolean hasNextPage() { return getPageNoSchema().hasNextPage(); @@ -151,6 +158,7 @@ public TableResult getNextPage() { .setTotalBytesProcessed(getTotalBytesProcessed()) .setTotalSlotMs(getTotalSlotMs()) .setNumDmlAffectedRows(getNumDmlAffectedRows()) + .setSessionInfo(getSessionInfo()) .build(); } return null; @@ -194,6 +202,7 @@ public String toString() { .add("totalBytesProcessed", getTotalBytesProcessed()) .add("totalSlotMs", getTotalSlotMs()) .add("numDmlAffectedRows", getNumDmlAffectedRows()) + .add("sessionInfo", getSessionInfo()) .toString(); } @@ -209,7 +218,8 @@ public final int hashCode() { getTotalBytesBilled(), getTotalBytesProcessed(), getTotalSlotMs(), - getNumDmlAffectedRows()); + getNumDmlAffectedRows(), + getSessionInfo()); } @Override @@ -231,6 +241,7 @@ && getTotalRows() == response.getTotalRows() && Objects.equals(getTotalBytesBilled(), response.getTotalBytesBilled()) && Objects.equals(getTotalBytesProcessed(), response.getTotalBytesProcessed()) && Objects.equals(getTotalSlotMs(), response.getTotalSlotMs()) - && Objects.equals(getNumDmlAffectedRows(), response.getNumDmlAffectedRows()); + && Objects.equals(getNumDmlAffectedRows(), response.getNumDmlAffectedRows()) + && Objects.equals(getSessionInfo(), response.getSessionInfo()); } } diff --git a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java index 434d317c420f..9a398e74a67d 100644 --- a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java +++ b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java @@ -52,6 +52,7 @@ import com.google.api.services.bigquery.model.ProjectList; import com.google.api.services.bigquery.model.ProjectReference; import com.google.api.services.bigquery.model.QueryRequest; +import com.google.api.services.bigquery.model.SessionInfo; import com.google.api.services.bigquery.model.TableCell; import com.google.api.services.bigquery.model.TableDataInsertAllRequest; import com.google.api.services.bigquery.model.TableDataInsertAllResponse; @@ -170,6 +171,8 @@ public class BigQueryImplTest { .setField("timestampField"); private static final TimePartitioning TIME_PARTITIONING_NULL_TYPE = TimePartitioning.fromPb(PB_TIMEPARTITIONING); + private static final String SESSION_ID = "test-session-id"; + private static final SessionInfo PB_SESSION_INFO = new SessionInfo().setSessionId(SESSION_ID); private static final ImmutableMap LABELS = ImmutableMap.of("key", "value"); private static final StandardTableDefinition TABLE_DEFINITION_WITH_PARTITIONING = StandardTableDefinition.newBuilder() @@ -2880,6 +2883,7 @@ void testQueryWithTimeoutSetsTimeout() throws InterruptedException, IOException .setTotalBytesProcessed(42L) .setTotalSlotMs(50L) .setNumDmlAffectedRows(0L) + .setSessionInfo(PB_SESSION_INFO) .setTotalRows(BigInteger.valueOf(1L)); when(bigqueryRpcMock.queryRpcSkipExceptionTranslation(eq(PROJECT), requestPbCapture.capture())) @@ -2894,6 +2898,8 @@ void testQueryWithTimeoutSetsTimeout() throws InterruptedException, IOException assertEquals((Long) 42L, tableResult.getTotalBytesProcessed()); assertEquals((Long) 50L, tableResult.getTotalSlotMs()); assertEquals((Long) 0L, tableResult.getNumDmlAffectedRows()); + assertNotNull(tableResult.getSessionInfo()); + assertEquals(SESSION_ID, tableResult.getSessionInfo().getSessionId()); QueryRequest requestPb = requestPbCapture.getValue(); assertEquals((Long) 1000L, requestPb.getTimeoutMs()); } diff --git a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/TableResultTest.java b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/TableResultTest.java index fe9d33d519ec..f697049dc2f9 100644 --- a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/TableResultTest.java +++ b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/TableResultTest.java @@ -21,6 +21,8 @@ import com.google.api.gax.paging.Page; import com.google.cloud.PageImpl; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics.StatementType; +import com.google.cloud.bigquery.JobStatistics.SessionInfo; import com.google.common.collect.ImmutableList; import org.junit.jupiter.api.Test; @@ -46,6 +48,15 @@ public Page getNextPage() { null, ImmutableList.of(newFieldValueList("2"))); private static final Schema SCHEMA = Schema.of(Field.of("field", LegacySQLTypeName.INTEGER)); + private static final String SESSION_ID = "session_123"; + private static final SessionInfo SESSION_INFO = + SessionInfo.newBuilder().setSessionId(SESSION_ID).build(); + private static final String SESSION_ID_1 = "session_1"; + private static final SessionInfo SESSION_INFO_1 = + SessionInfo.newBuilder().setSessionId(SESSION_ID_1).build(); + private static final String SESSION_ID_2 = "session_2"; + private static final SessionInfo SESSION_INFO_2 = + SessionInfo.newBuilder().setSessionId(SESSION_ID_2).build(); private static FieldValueList newFieldValueList(String s) { return FieldValueList.of(ImmutableList.of(FieldValue.of(PRIMITIVE, s))); @@ -124,27 +135,29 @@ void testStatementTypeAndExecutionStats() { .setTotalRows(3L) .setPageNoSchema(INNER_PAGE_0) .setRowsInPage(2L) - .setStatementType(JobStatistics.QueryStatistics.StatementType.SELECT) + .setStatementType(StatementType.SELECT) .setTotalBytesBilled(1024L) .setTotalBytesProcessed(2048L) .setTotalSlotMs(500L) .setNumDmlAffectedRows(0L) + .setSessionInfo(SESSION_INFO) .build(); - assertThat(result.getStatementType()) - .isEqualTo(JobStatistics.QueryStatistics.StatementType.SELECT); + assertThat(result.getStatementType()).isEqualTo(StatementType.SELECT); assertThat(result.getTotalBytesBilled()).isEqualTo(1024L); assertThat(result.getTotalBytesProcessed()).isEqualTo(2048L); assertThat(result.getTotalSlotMs()).isEqualTo(500L); assertThat(result.getNumDmlAffectedRows()).isEqualTo(0L); + assertThat(result.getSessionInfo()).isEqualTo(SESSION_INFO); + assertThat(result.getSessionInfo().getSessionId()).isEqualTo(SESSION_ID); TableResult next = result.getNextPage(); - assertThat(next.getStatementType()) - .isEqualTo(JobStatistics.QueryStatistics.StatementType.SELECT); + assertThat(next.getStatementType()).isEqualTo(StatementType.SELECT); assertThat(next.getTotalBytesBilled()).isEqualTo(1024L); assertThat(next.getTotalBytesProcessed()).isEqualTo(2048L); assertThat(next.getTotalSlotMs()).isEqualTo(500L); assertThat(next.getNumDmlAffectedRows()).isEqualTo(0L); + assertThat(next.getSessionInfo()).isEqualTo(SESSION_INFO); } @Test @@ -155,23 +168,24 @@ void testToBuilder() { .setTotalRows(3L) .setPageNoSchema(INNER_PAGE_0) .setRowsInPage(2L) - .setStatementType(JobStatistics.QueryStatistics.StatementType.INSERT) + .setStatementType(StatementType.INSERT) .setTotalBytesBilled(500L) .setTotalBytesProcessed(1000L) .setTotalSlotMs(250L) .setNumDmlAffectedRows(5L) + .setSessionInfo(SESSION_INFO) .build(); TableResult modified = result.toBuilder() - .setStatementType(JobStatistics.QueryStatistics.StatementType.UPDATE) + .setStatementType(StatementType.UPDATE) .setNumDmlAffectedRows(10L) .build(); - assertThat(modified.getStatementType()) - .isEqualTo(JobStatistics.QueryStatistics.StatementType.UPDATE); + assertThat(modified.getStatementType()).isEqualTo(StatementType.UPDATE); assertThat(modified.getNumDmlAffectedRows()).isEqualTo(10L); assertThat(modified.getTotalBytesBilled()).isEqualTo(500L); + assertThat(modified.getSessionInfo()).isEqualTo(SESSION_INFO); } @Test @@ -182,11 +196,12 @@ void testEqualsAndHashCode() { .setTotalRows(3L) .setPageNoSchema(INNER_PAGE_0) .setRowsInPage(2L) - .setStatementType(JobStatistics.QueryStatistics.StatementType.SELECT) + .setStatementType(StatementType.SELECT) .setTotalBytesBilled(100L) .setTotalBytesProcessed(200L) .setTotalSlotMs(50L) .setNumDmlAffectedRows(0L) + .setSessionInfo(SESSION_INFO_1) .build(); TableResult result2 = @@ -195,11 +210,12 @@ void testEqualsAndHashCode() { .setTotalRows(3L) .setPageNoSchema(INNER_PAGE_0) .setRowsInPage(2L) - .setStatementType(JobStatistics.QueryStatistics.StatementType.SELECT) + .setStatementType(StatementType.SELECT) .setTotalBytesBilled(100L) .setTotalBytesProcessed(200L) .setTotalSlotMs(50L) .setNumDmlAffectedRows(0L) + .setSessionInfo(SESSION_INFO_1) .build(); TableResult result3 = @@ -208,11 +224,12 @@ void testEqualsAndHashCode() { .setTotalRows(3L) .setPageNoSchema(INNER_PAGE_0) .setRowsInPage(2L) - .setStatementType(JobStatistics.QueryStatistics.StatementType.DELETE) + .setStatementType(StatementType.DELETE) .setTotalBytesBilled(100L) .setTotalBytesProcessed(200L) .setTotalSlotMs(50L) .setNumDmlAffectedRows(1L) + .setSessionInfo(SESSION_INFO_2) .build(); assertThat(result1).isEqualTo(result2); @@ -220,5 +237,6 @@ void testEqualsAndHashCode() { assertThat(result1).isNotEqualTo(result3); assertThat(result1.toString()).contains("statementType=SELECT"); assertThat(result1.toString()).contains("totalBytesBilled=100"); + assertThat(result1.toString()).contains("sessionId=" + SESSION_ID_1); } } From bd82fb03880fb338f20aa8139086f11b1703efb2 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Fri, 21 Aug 2026 18:02:33 +0000 Subject: [PATCH 3/4] chore: make setters package private and use jspecify notation --- .../google/cloud/bigquery/TableResult.java | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java index 66ac8678bf19..39904e35d1ef 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java @@ -26,7 +26,7 @@ import com.google.common.collect.Iterators; import java.io.Serializable; import java.util.Objects; -import javax.annotation.Nullable; +import org.jspecify.annotations.Nullable; @AutoValue public abstract class TableResult implements Page, Serializable { @@ -53,17 +53,17 @@ public abstract static class Builder { abstract TableResult.Builder setRowsInPage(Long rowsInPage); - public abstract TableResult.Builder setStatementType(StatementType statementType); + abstract TableResult.Builder setStatementType(@Nullable StatementType statementType); - public abstract TableResult.Builder setTotalBytesBilled(Long totalBytesBilled); + abstract TableResult.Builder setTotalBytesBilled(@Nullable Long totalBytesBilled); - public abstract TableResult.Builder setTotalBytesProcessed(Long totalBytesProcessed); + abstract TableResult.Builder setTotalBytesProcessed(@Nullable Long totalBytesProcessed); - public abstract TableResult.Builder setTotalSlotMs(Long totalSlotMs); + abstract TableResult.Builder setTotalSlotMs(@Nullable Long totalSlotMs); - public abstract TableResult.Builder setNumDmlAffectedRows(Long numDmlAffectedRows); + abstract TableResult.Builder setNumDmlAffectedRows(@Nullable Long numDmlAffectedRows); - public abstract TableResult.Builder setSessionInfo(SessionInfo sessionInfo); + abstract TableResult.Builder setSessionInfo(@Nullable SessionInfo sessionInfo); /** Creates a @code TableResult} object. */ public abstract TableResult build(); @@ -76,8 +76,7 @@ public static Builder newBuilder() { } /** Returns the schema of the results. Null if the schema is not supplied. */ - @Nullable - public abstract Schema getSchema(); + public abstract @Nullable Schema getSchema(); /** * Returns the total number of rows in the complete result set, which can be more than the number @@ -88,48 +87,38 @@ public static Builder newBuilder() { public abstract Page getPageNoSchema(); - @Nullable - public abstract JobId getJobId(); + public abstract @Nullable JobId getJobId(); - @Nullable - public abstract String getQueryId(); + public abstract @Nullable String getQueryId(); - @Nullable - public abstract JobCreationReason getJobCreationReason(); + public abstract @Nullable JobCreationReason getJobCreationReason(); /** Returns the number of rows in the current page of results. */ - @Nullable - public abstract Long getRowsInPage(); + public abstract @Nullable Long getRowsInPage(); /** * Returns the statement type of the query (e.g. SELECT, INSERT, UPDATE, DDL, SCRIPT), if * available. */ - @Nullable - public abstract StatementType getStatementType(); + public abstract @Nullable StatementType getStatementType(); /** Returns the total number of bytes billed for the query, if available. */ - @Nullable - public abstract Long getTotalBytesBilled(); + public abstract @Nullable Long getTotalBytesBilled(); /** Returns the total number of bytes processed by the query, if available. */ - @Nullable - public abstract Long getTotalBytesProcessed(); + public abstract @Nullable Long getTotalBytesProcessed(); /** Returns the total slot milliseconds for the query, if available. */ - @Nullable - public abstract Long getTotalSlotMs(); + public abstract @Nullable Long getTotalSlotMs(); /** * Returns the number of rows affected by a DML statement (INSERT, UPDATE, DELETE, MERGE), if * available. */ - @Nullable - public abstract Long getNumDmlAffectedRows(); + public abstract @Nullable Long getNumDmlAffectedRows(); /** Returns information about the session if this query is part of one, if available. */ - @Nullable - public abstract SessionInfo getSessionInfo(); + public abstract @Nullable SessionInfo getSessionInfo(); @Override public boolean hasNextPage() { From 3ea80a9c82baf97f5072ca194c50cf7dc8a8e6f8 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Fri, 21 Aug 2026 19:04:26 +0000 Subject: [PATCH 4/4] update javadoc --- .../google/cloud/bigquery/TableResult.java | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java index 39904e35d1ef..b49a253e0c67 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/TableResult.java @@ -97,27 +97,47 @@ public static Builder newBuilder() { public abstract @Nullable Long getRowsInPage(); /** - * Returns the statement type of the query (e.g. SELECT, INSERT, UPDATE, DDL, SCRIPT), if - * available. + * Returns the statement type of the query (e.g. SELECT, INSERT, UPDATE, DDL, SCRIPT). + * + * @return statement type, or {@code null} if not populated by the service */ public abstract @Nullable StatementType getStatementType(); - /** Returns the total number of bytes billed for the query, if available. */ + /** + * Returns the total number of bytes billed for the query. + * + * @return total bytes billed, or {@code null} if not populated by the service + */ public abstract @Nullable Long getTotalBytesBilled(); - /** Returns the total number of bytes processed by the query, if available. */ + /** + * Returns the total number of bytes processed by the query. + * + * @return total bytes processed, or {@code null} if not populated by the service + */ public abstract @Nullable Long getTotalBytesProcessed(); - /** Returns the total slot milliseconds for the query, if available. */ + /** + * Returns the total slot milliseconds consumed by the query. + * + * @return total slot milliseconds, or {@code null} if not populated by the service + */ public abstract @Nullable Long getTotalSlotMs(); /** - * Returns the number of rows affected by a DML statement (INSERT, UPDATE, DELETE, MERGE), if - * available. + * Returns the number of rows affected by a DML statement (INSERT, UPDATE, DELETE, MERGE). + * + * @return number of affected rows for DML queries, or {@code null} if not populated by the + * service */ public abstract @Nullable Long getNumDmlAffectedRows(); - /** Returns information about the session if this query is part of one, if available. */ + /** + * Returns information about the BigQuery session if this query was executed within or created a + * session. + * + * @return session information, or {@code null} if not populated by the service + */ public abstract @Nullable SessionInfo getSessionInfo(); @Override