Skip to content

Commit 465ab61

Browse files
committed
Add new QueryMode's support in client library
1 parent 7a8a29b commit 465ab61

File tree

6 files changed

+81
-7
lines changed

6 files changed

+81
-7
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,12 @@ public final ResultSet analyzeQuery(Statement statement, QueryAnalyzeMode readCo
618618
case PLAN:
619619
return executeQueryInternal(
620620
statement, com.google.spanner.v1.ExecuteSqlRequest.QueryMode.PLAN);
621+
case WITH_STATS:
622+
return executeQueryInternal(
623+
statement, com.google.spanner.v1.ExecuteSqlRequest.QueryMode.WITH_STATS);
624+
case WITH_PLAN_AND_STATS:
625+
return executeQueryInternal(
626+
statement, com.google.spanner.v1.ExecuteSqlRequest.QueryMode.WITH_PLAN_AND_STATS);
621627
default:
622628
throw new IllegalStateException(
623629
"Unknown value for QueryAnalyzeMode : " + readContextQueryMode);

google-cloud-spanner/src/main/java/com/google/cloud/spanner/ReadContext.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,17 @@ enum QueryAnalyzeMode {
3434
/** Retrieves only the query plan information. No result data is returned. */
3535
PLAN,
3636
/** Retrieves both query plan and query execution statistics along with the result data. */
37-
PROFILE
37+
PROFILE,
38+
/**
39+
* Retrieves the overall (but not operator-level) execution statistics along with the result
40+
* data.
41+
*/
42+
WITH_STATS,
43+
/**
44+
* Retrieves the query plan, overall (but not operator-level) execution statistics along with
45+
* the result data.
46+
*/
47+
WITH_PLAN_AND_STATS
3848
}
3949
/**
4050
* Reads zero or more rows from a database.

google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSet.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ public interface ResultSet extends AutoCloseable, StructReader {
6363
void close();
6464

6565
/**
66-
* Returns the {@link ResultSetStats} for the query only if the query was executed in either the
67-
* {@code PLAN} or the {@code PROFILE} mode via the {@link ReadContext#analyzeQuery(Statement,
66+
* Returns the {@link ResultSetStats} for the query only if the query was executed in {@code
67+
* PLAN}, {@code PROFILE}, {@code WITH_STATS} or the {@code WITH_PLAN_AND_STATS} mode via the
68+
* {@link ReadContext#analyzeQuery(Statement,
6869
* com.google.cloud.spanner.ReadContext.QueryAnalyzeMode)} method or for DML statements in {@link
6970
* ReadContext#executeQuery(Statement, QueryOption...)}. Attempts to call this method on a {@code
7071
* ResultSet} not obtained from {@code analyzeQuery} or {@code executeQuery} will return a {@code

google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,12 @@ private ResultSet internalAnalyzeStatement(
878878
case PROFILE:
879879
queryMode = QueryMode.PROFILE;
880880
break;
881+
case WITH_STATS:
882+
queryMode = QueryMode.WITH_STATS;
883+
break;
884+
case WITH_PLAN_AND_STATS:
885+
queryMode = QueryMode.WITH_PLAN_AND_STATS;
886+
break;
881887
default:
882888
throw SpannerExceptionFactory.newSpannerException(
883889
ErrorCode.INVALID_ARGUMENT, "Unknown analyze mode: " + analyzeMode);

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AnalyzeMode.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,27 @@
1919
import com.google.cloud.spanner.ReadContext.QueryAnalyzeMode;
2020

2121
/**
22-
* {@link AnalyzeMode} indicates whether a query should be executed as a normal query (NONE),
23-
* whether only a query plan should be returned, or whether the query should be profiled while
24-
* executed.
22+
* {@link AnalyzeMode} controls the execution and returned information for a query:
23+
*
24+
* <ul>
25+
* <li>{@code NONE}: The default mode. Only the statement results are returned.
26+
* <li>{@code PLAN}: Returns only the query plan, without any results or execution statistics
27+
* information.
28+
* <li>{@code PROFILE}: Returns the query plan, overall execution statistics, operator-level
29+
* execution statistics along with the results. This mode has a performance overhead and is
30+
* not recommended for production traffic.
31+
* <li>{@code WITH_STATS}: Returns the overall (but not operator-level) execution statistics along
32+
* with the results.
33+
* <li>{@code WITH_PLAN_AND_STATS}: Returns the query plan, overall (but not operator-level)
34+
* execution statistics along with the results.
35+
* </ul>
2536
*/
2637
enum AnalyzeMode {
2738
NONE(null),
2839
PLAN(QueryAnalyzeMode.PLAN),
29-
PROFILE(QueryAnalyzeMode.PROFILE);
40+
PROFILE(QueryAnalyzeMode.PROFILE),
41+
WITH_STATS(QueryAnalyzeMode.WITH_STATS),
42+
WITH_PLAN_AND_STATS(QueryAnalyzeMode.WITH_PLAN_AND_STATS);
3043

3144
private final QueryAnalyzeMode mode;
3245

@@ -45,6 +58,10 @@ static AnalyzeMode of(QueryAnalyzeMode mode) {
4558
return AnalyzeMode.PLAN;
4659
case PROFILE:
4760
return AnalyzeMode.PROFILE;
61+
case WITH_STATS:
62+
return AnalyzeMode.WITH_STATS;
63+
case WITH_PLAN_AND_STATS:
64+
return AnalyzeMode.WITH_PLAN_AND_STATS;
4865
default:
4966
throw new IllegalArgumentException(mode + " is unknown");
5067
}

google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3538,6 +3538,40 @@ public void testBackendQueryOptionsWithAnalyzeQuery() {
35383538
}
35393539
}
35403540

3541+
@Test
3542+
public void testWithStatsQueryModeWithAnalyzeQuery() {
3543+
// Use a Spanner instance with MinSession=0 to prevent background requests
3544+
// from the session pool interfering with the test case.
3545+
try (Spanner spanner =
3546+
SpannerOptions.newBuilder()
3547+
.setProjectId("[PROJECT]")
3548+
.setChannelProvider(channelProvider)
3549+
.setCredentials(NoCredentials.getInstance())
3550+
.setSessionPoolOption(SessionPoolOptions.newBuilder().setMinSessions(0).build())
3551+
.build()
3552+
.getService()) {
3553+
DatabaseClient client =
3554+
spanner.getDatabaseClient(DatabaseId.of("[PROJECT]", "[INSTANCE]", "[DATABASE"));
3555+
try (ReadOnlyTransaction tx = client.readOnlyTransaction()) {
3556+
try (ResultSet rs =
3557+
tx.analyzeQuery(
3558+
Statement.newBuilder(SELECT1.getSql())
3559+
.build(),
3560+
QueryAnalyzeMode.WITH_STATS)) {
3561+
// Just iterate over the results to execute the query.
3562+
consumeResults(rs);
3563+
}
3564+
}
3565+
// Check that the last query was executed using a custom optimizer version and statistics
3566+
// package.
3567+
List<AbstractMessage> requests = mockSpanner.getRequests();
3568+
assertThat(requests).isNotEmpty();
3569+
assertThat(requests.get(requests.size() - 1)).isInstanceOf(ExecuteSqlRequest.class);
3570+
ExecuteSqlRequest request = (ExecuteSqlRequest) requests.get(requests.size() - 1);
3571+
assertThat(request.getQueryMode()).isEqualTo(QueryMode.WITH_STATS);
3572+
}
3573+
}
3574+
35413575
@Test
35423576
public void testBackendPartitionQueryOptions() {
35433577
// Use a Spanner instance with MinSession=0 to prevent background requests

0 commit comments

Comments
 (0)