Skip to content

Commit e294532

Browse files
authored
feat: adds query optimizer statistics support (#385)
* feat: adds optimizer_statistics_package option Adds the possibility to set the optimizer statistics package when executing queries. This option can be set in different levels as follows: 1. Through statement hints 2. Through query level configuration 3. Through an environment variable 4. Through application level configuration If more than one package is set (in different levels) the precedence is from 1 to 4 (1 has top priority). * test: adds integration tests Adds integration tests for setting the query options optimizer statistics package. * fix: addresses PR comments Fix missing value in the documentation of the optimizer statistics package for the connection class. * fix: adds tests for invalid stats packages Adds tests for invalid statistics packages (whitespace only ones). * fix: adds integration tests for query options Adds an integration test to run the sql script with several expectations for the query options. These are tests for the optimizer version and optimizer statistics package. * fix: formatting of ClientSideStatements.json This file is using a mix of tabs and spaces. For now I have formatted as the other lines, so that the diff is clear. In a further PR I will reformat the whole file to use only spaces. * tests: fix Connection test Fixes connection test when using environment variables for retrieving configurations. This only works when a first connection is created, so we moved this specific test to its own subclass. * fix: fix clirr checks Provides default interface implementations and fixes clirr checks
1 parent 8a23ad0 commit e294532

22 files changed

+568
-50
lines changed

google-cloud-spanner/clirr-ignored-differences.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,4 +618,23 @@
618618
<className>com/google/cloud/spanner/TransactionContext</className>
619619
<method>com.google.api.core.ApiFuture bufferAsync(java.lang.Iterable)</method>
620620
</difference>
621+
622+
<!-- Query stats optimiser statistics package -->
623+
<!-- These are not breaking changes, since we provide default interface implementation -->
624+
<difference>
625+
<differenceType>7012</differenceType>
626+
<className>com/google/cloud/spanner/SpannerOptions$SpannerEnvironment</className>
627+
<method>java.lang.String getOptimizerStatisticsPackage()</method>
628+
</difference>
629+
<difference>
630+
<differenceType>7012</differenceType>
631+
<className>com/google/cloud/spanner/connection/Connection</className>
632+
<method>java.lang.String getOptimizerStatisticsPackage()</method>
633+
</difference>
634+
<difference>
635+
<differenceType>7012</differenceType>
636+
<className>com/google/cloud/spanner/connection/Connection</className>
637+
<method>void setOptimizerStatisticsPackage(java.lang.String)</method>
638+
</difference>
639+
621640
</differences>

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,15 @@ public interface SpannerEnvironment {
585585
*/
586586
@Nonnull
587587
String getOptimizerVersion();
588+
589+
/**
590+
* The optimizer statistics package to use. Must return an empty string to indicate that no
591+
* value has been set.
592+
*/
593+
@Nonnull
594+
default String getOptimizerStatisticsPackage() {
595+
throw new UnsupportedOperationException("Unimplemented");
596+
}
588597
}
589598

590599
/**
@@ -594,13 +603,21 @@ public interface SpannerEnvironment {
594603
private static class SpannerEnvironmentImpl implements SpannerEnvironment {
595604
private static final SpannerEnvironmentImpl INSTANCE = new SpannerEnvironmentImpl();
596605
private static final String SPANNER_OPTIMIZER_VERSION_ENV_VAR = "SPANNER_OPTIMIZER_VERSION";
606+
private static final String SPANNER_OPTIMIZER_STATISTICS_PACKAGE_ENV_VAR =
607+
"SPANNER_OPTIMIZER_STATISTICS_PACKAGE";
597608

598609
private SpannerEnvironmentImpl() {}
599610

600611
@Override
601612
public String getOptimizerVersion() {
602613
return MoreObjects.firstNonNull(System.getenv(SPANNER_OPTIMIZER_VERSION_ENV_VAR), "");
603614
}
615+
616+
@Override
617+
public String getOptimizerStatisticsPackage() {
618+
return MoreObjects.firstNonNull(
619+
System.getenv(SPANNER_OPTIMIZER_STATISTICS_PACKAGE_ENV_VAR), "");
620+
}
604621
}
605622

606623
/** Builder for {@link SpannerOptions} instances. */
@@ -957,6 +974,7 @@ public Builder setDefaultQueryOptions(DatabaseId database, QueryOptions defaultQ
957974
QueryOptions getEnvironmentQueryOptions() {
958975
return QueryOptions.newBuilder()
959976
.setOptimizerVersion(environment.getOptimizerVersion())
977+
.setOptimizerStatisticsPackage(environment.getOptimizerStatisticsPackage())
960978
.build();
961979
}
962980

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@
9696
* <li><code>
9797
* SET OPTIMIZER_VERSION='&lt;version&gt;' | 'LATEST'
9898
* </code>: Sets the value of <code>OPTIMIZER_VERSION</code> for this connection.
99+
* <li><code>SHOW OPTIMIZER_STATISTICS_PACKAGE</code>: Returns the current value of <code>
100+
* OPTIMIZER_STATISTICS_PACKAGE</code> of this connection as a {@link ResultSet}
101+
* <li><code>
102+
* SET OPTIMIZER_STATISTICS_PACKAGE='&lt;package&gt;' | ''
103+
* </code>: Sets the value of <code>OPTIMIZER_STATISTICS_PACKAGE</code> for this connection.
99104
* <li><code>BEGIN [TRANSACTION]</code>: Begins a new transaction. This statement is optional when
100105
* the connection is not in autocommit mode, as a new transaction will automatically be
101106
* started when a query or update statement is issued. In autocommit mode, this statement will
@@ -448,6 +453,29 @@ public interface Connection extends AutoCloseable {
448453
*/
449454
String getOptimizerVersion();
450455

456+
/**
457+
* Sets the query optimizer statistics package
458+
*
459+
* @param optimizerStatisticsPackage The query optimizer statistics package to use. Must be a
460+
* string composed of letters, numbers, dashes and underscores or an empty string. The empty
461+
* string will instruct the connection to use the optimizer statistics package that is defined
462+
* the environment variable <code>SPANNER_OPTIMIZER_STATISTICS_PACKAGE</code>. If no value is
463+
* specified in the environment variable, the client level query optimizer is used. If none is
464+
* set, the default query optimizer of Cloud Spanner is used.
465+
*/
466+
default void setOptimizerStatisticsPackage(String optimizerStatisticsPackage) {
467+
throw new UnsupportedOperationException("Unimplemented");
468+
}
469+
470+
/**
471+
* Gets the current query optimizer statistics package of this connection.
472+
*
473+
* @return The query optimizer statistics package that is currently used by this connection.
474+
*/
475+
default String getOptimizerStatisticsPackage() {
476+
throw new UnsupportedOperationException("Unimplemented");
477+
}
478+
451479
/**
452480
* Sets whether this connection should request commit statistics from Cloud Spanner for read/write
453481
* transactions and DML statements in autocommit mode.

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,20 @@ public String getOptimizerVersion() {
433433
return this.queryOptions.getOptimizerVersion();
434434
}
435435

436+
@Override
437+
public void setOptimizerStatisticsPackage(String optimizerStatisticsPackage) {
438+
Preconditions.checkNotNull(optimizerStatisticsPackage);
439+
ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
440+
this.queryOptions =
441+
queryOptions.toBuilder().setOptimizerStatisticsPackage(optimizerStatisticsPackage).build();
442+
}
443+
444+
@Override
445+
public String getOptimizerStatisticsPackage() {
446+
ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
447+
return this.queryOptions.getOptimizerStatisticsPackage();
448+
}
449+
436450
@Override
437451
public void setStatementTimeout(long timeout, TimeUnit unit) {
438452
Preconditions.checkArgument(timeout > 0L, "Zero or negative timeout values are not allowed");

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ public String[] getValidValues() {
157157
private static final String DEFAULT_NUM_CHANNELS = null;
158158
private static final String DEFAULT_USER_AGENT = null;
159159
private static final String DEFAULT_OPTIMIZER_VERSION = "";
160+
private static final String DEFAULT_OPTIMIZER_STATISTICS_PACKAGE = "";
160161
private static final boolean DEFAULT_RETURN_COMMIT_STATS = false;
161162
private static final boolean DEFAULT_LENIENT = false;
162163

@@ -190,6 +191,9 @@ public String[] getValidValues() {
190191
private static final String USER_AGENT_PROPERTY_NAME = "userAgent";
191192
/** Query optimizer version to use for a connection. */
192193
private static final String OPTIMIZER_VERSION_PROPERTY_NAME = "optimizerVersion";
194+
/** Query optimizer statistics package to use for a connection. */
195+
private static final String OPTIMIZER_STATISTICS_PACKAGE_PROPERTY_NAME =
196+
"optimizerStatisticsPackage";
193197
/** Name of the 'lenientMode' connection property. */
194198
public static final String LENIENT_PROPERTY_NAME = "lenient";
195199

@@ -238,6 +242,8 @@ public String[] getValidValues() {
238242
ConnectionProperty.createStringProperty(
239243
OPTIMIZER_VERSION_PROPERTY_NAME,
240244
"Sets the default query optimizer version to use for this connection."),
245+
ConnectionProperty.createStringProperty(
246+
OPTIMIZER_STATISTICS_PACKAGE_PROPERTY_NAME, ""),
241247
ConnectionProperty.createBooleanProperty("returnCommitStats", "", false),
242248
ConnectionProperty.createBooleanProperty(
243249
"autoConfigEmulator",
@@ -521,6 +527,7 @@ private ConnectionOptions(Builder builder) {
521527
this.userAgent = parseUserAgent(this.uri);
522528
QueryOptions.Builder queryOptionsBuilder = QueryOptions.newBuilder();
523529
queryOptionsBuilder.setOptimizerVersion(parseOptimizerVersion(this.uri));
530+
queryOptionsBuilder.setOptimizerStatisticsPackage(parseOptimizerStatisticsPackage(this.uri));
524531
this.queryOptions = queryOptionsBuilder.build();
525532
this.returnCommitStats = parseReturnCommitStats(this.uri);
526533
this.autoConfigEmulator = parseAutoConfigEmulator(this.uri);
@@ -695,6 +702,12 @@ static String parseOptimizerVersion(String uri) {
695702
return value != null ? value : DEFAULT_OPTIMIZER_VERSION;
696703
}
697704

705+
@VisibleForTesting
706+
static String parseOptimizerStatisticsPackage(String uri) {
707+
String value = parseUriProperty(uri, OPTIMIZER_STATISTICS_PACKAGE_PROPERTY_NAME);
708+
return value != null ? value : DEFAULT_OPTIMIZER_STATISTICS_PACKAGE;
709+
}
710+
698711
@VisibleForTesting
699712
static boolean parseReturnCommitStats(String uri) {
700713
String value = parseUriProperty(uri, "returnCommitStats");

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ interface ConnectionStatementExecutor {
6666

6767
StatementResult statementShowOptimizerVersion();
6868

69+
StatementResult statementSetOptimizerStatisticsPackage(String optimizerStatisticsPackage);
70+
71+
StatementResult statementShowOptimizerStatisticsPackage();
72+
6973
StatementResult statementSetReturnCommitStats(Boolean returnCommitStats);
7074

7175
StatementResult statementShowReturnCommitStats();

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.RUN_BATCH;
2424
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_AUTOCOMMIT;
2525
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_AUTOCOMMIT_DML_MODE;
26+
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_OPTIMIZER_STATISTICS_PACKAGE;
2627
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_OPTIMIZER_VERSION;
2728
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_READONLY;
2829
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_READ_ONLY_STALENESS;
@@ -34,6 +35,7 @@
3435
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_AUTOCOMMIT_DML_MODE;
3536
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_COMMIT_RESPONSE;
3637
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_COMMIT_TIMESTAMP;
38+
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_OPTIMIZER_STATISTICS_PACKAGE;
3739
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_OPTIMIZER_VERSION;
3840
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_READONLY;
3941
import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_READ_ONLY_STALENESS;
@@ -230,6 +232,20 @@ public StatementResult statementShowOptimizerVersion() {
230232
"OPTIMIZER_VERSION", getConnection().getOptimizerVersion(), SHOW_OPTIMIZER_VERSION);
231233
}
232234

235+
@Override
236+
public StatementResult statementSetOptimizerStatisticsPackage(String optimizerStatisticsPackage) {
237+
getConnection().setOptimizerStatisticsPackage(optimizerStatisticsPackage);
238+
return noResult(SET_OPTIMIZER_STATISTICS_PACKAGE);
239+
}
240+
241+
@Override
242+
public StatementResult statementShowOptimizerStatisticsPackage() {
243+
return resultSet(
244+
"OPTIMIZER_STATISTICS_PACKAGE",
245+
getConnection().getOptimizerStatisticsPackage(),
246+
SHOW_OPTIMIZER_STATISTICS_PACKAGE);
247+
}
248+
233249
@Override
234250
public StatementResult statementSetReturnCommitStats(Boolean returnCommitStats) {
235251
getConnection().setReturnCommitStats(returnCommitStats);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ enum ClientSideStatementType {
6565
SET_READ_ONLY_STALENESS,
6666
SHOW_OPTIMIZER_VERSION,
6767
SET_OPTIMIZER_VERSION,
68+
SHOW_OPTIMIZER_STATISTICS_PACKAGE,
69+
SET_OPTIMIZER_STATISTICS_PACKAGE,
6870
SHOW_RETURN_COMMIT_STATS,
6971
SET_RETURN_COMMIT_STATS,
7072
BEGIN,

google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@
7676
"method": "statementShowOptimizerVersion",
7777
"exampleStatements": ["show variable optimizer_version"]
7878
},
79+
{
80+
"name": "SHOW VARIABLE OPTIMIZER_STATISTICS_PACKAGE",
81+
"executorName": "ClientSideStatementNoParamExecutor",
82+
"resultType": "RESULT_SET",
83+
"regex": "(?is)\\A\\s*show\\s+variable\\s+optimizer_statistics_package\\s*\\z",
84+
"method": "statementShowOptimizerStatisticsPackage",
85+
"exampleStatements": ["show variable optimizer_statistics_package"]
86+
},
7987
{
8088
"name": "SHOW VARIABLE RETURN_COMMIT_STATS",
8189
"executorName": "ClientSideStatementNoParamExecutor",
@@ -281,6 +289,20 @@
281289
"converterName": "ClientSideStatementValueConverters$StringValueConverter"
282290
}
283291
},
292+
{
293+
"name": "SET OPTIMIZER_STATISTICS_PACKAGE = '<package>'|''",
294+
"executorName": "ClientSideStatementSetExecutor",
295+
"resultType": "NO_RESULT",
296+
"regex": "(?is)\\A\\s*set\\s+optimizer_statistics_package\\s*(?:=)\\s*(.*)\\z",
297+
"method": "statementSetOptimizerStatisticsPackage",
298+
"exampleStatements": ["set optimizer_statistics_package='auto_20191128_14_47_22UTC'", "set optimizer_statistics_package=''"],
299+
"setStatement": {
300+
"propertyName": "OPTIMIZER_STATISTICS_PACKAGE",
301+
"separator": "=",
302+
"allowedValues": "'((\\S+)|())'",
303+
"converterName": "ClientSideStatementValueConverters$StringValueConverter"
304+
}
305+
},
284306
{
285307
"name": "SET RETURN_COMMIT_STATS = TRUE|FALSE",
286308
"executorName": "ClientSideStatementSetExecutor",
@@ -296,4 +318,4 @@
296318
}
297319
}
298320
]
299-
}
321+
}

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ public static Collection<Object[]> parameters() {
5252
List<Object[]> params = new ArrayList<>();
5353
params.add(new Object[] {QueryOptions.getDefaultInstance()});
5454
params.add(
55-
new Object[] {QueryOptions.newBuilder().setOptimizerVersion("some-version").build()});
55+
new Object[] {
56+
QueryOptions.newBuilder()
57+
.setOptimizerVersion("some-version")
58+
.setOptimizerStatisticsPackage("some-package")
59+
.build()
60+
});
5661
return params;
5762
}
5863

@@ -134,14 +139,20 @@ public void executeSqlRequestBuilderWithQueryOptions() {
134139
context
135140
.getExecuteSqlRequestBuilder(
136141
Statement.newBuilder("SELECT FOO FROM BAR")
137-
.withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("2.0").build())
142+
.withQueryOptions(
143+
QueryOptions.newBuilder()
144+
.setOptimizerVersion("2.0")
145+
.setOptimizerStatisticsPackage("custom-package")
146+
.build())
138147
.build(),
139148
QueryMode.NORMAL,
140149
Options.fromQueryOptions(),
141150
true)
142151
.build();
143152
assertThat(request.getSql()).isEqualTo("SELECT FOO FROM BAR");
144153
assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("2.0");
154+
assertThat(request.getQueryOptions().getOptimizerStatisticsPackage())
155+
.isEqualTo("custom-package");
145156
}
146157

147158
@Test

0 commit comments

Comments
 (0)