diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index c6a936c51c..75f83555c9 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -618,4 +618,23 @@ com/google/cloud/spanner/TransactionContext com.google.api.core.ApiFuture bufferAsync(java.lang.Iterable) + + + + + 7012 + com/google/cloud/spanner/SpannerOptions$SpannerEnvironment + java.lang.String getOptimizerStatisticsPackage() + + + 7012 + com/google/cloud/spanner/connection/Connection + java.lang.String getOptimizerStatisticsPackage() + + + 7012 + com/google/cloud/spanner/connection/Connection + void setOptimizerStatisticsPackage(java.lang.String) + + diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java index 88587c6428..3274048c61 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java @@ -585,6 +585,15 @@ public interface SpannerEnvironment { */ @Nonnull String getOptimizerVersion(); + + /** + * The optimizer statistics package to use. Must return an empty string to indicate that no + * value has been set. + */ + @Nonnull + default String getOptimizerStatisticsPackage() { + throw new UnsupportedOperationException("Unimplemented"); + } } /** @@ -594,6 +603,8 @@ public interface SpannerEnvironment { private static class SpannerEnvironmentImpl implements SpannerEnvironment { private static final SpannerEnvironmentImpl INSTANCE = new SpannerEnvironmentImpl(); private static final String SPANNER_OPTIMIZER_VERSION_ENV_VAR = "SPANNER_OPTIMIZER_VERSION"; + private static final String SPANNER_OPTIMIZER_STATISTICS_PACKAGE_ENV_VAR = + "SPANNER_OPTIMIZER_STATISTICS_PACKAGE"; private SpannerEnvironmentImpl() {} @@ -601,6 +612,12 @@ private SpannerEnvironmentImpl() {} public String getOptimizerVersion() { return MoreObjects.firstNonNull(System.getenv(SPANNER_OPTIMIZER_VERSION_ENV_VAR), ""); } + + @Override + public String getOptimizerStatisticsPackage() { + return MoreObjects.firstNonNull( + System.getenv(SPANNER_OPTIMIZER_STATISTICS_PACKAGE_ENV_VAR), ""); + } } /** Builder for {@link SpannerOptions} instances. */ @@ -957,6 +974,7 @@ public Builder setDefaultQueryOptions(DatabaseId database, QueryOptions defaultQ QueryOptions getEnvironmentQueryOptions() { return QueryOptions.newBuilder() .setOptimizerVersion(environment.getOptimizerVersion()) + .setOptimizerStatisticsPackage(environment.getOptimizerStatisticsPackage()) .build(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java index 813dda25cc..fb7e7bca01 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java @@ -96,6 +96,11 @@ *
  • * SET OPTIMIZER_VERSION='<version>' | 'LATEST' * : Sets the value of OPTIMIZER_VERSION for this connection. + *
  • SHOW OPTIMIZER_STATISTICS_PACKAGE: Returns the current value of + * OPTIMIZER_STATISTICS_PACKAGE of this connection as a {@link ResultSet} + *
  • + * SET OPTIMIZER_STATISTICS_PACKAGE='<package>' | '' + * : Sets the value of OPTIMIZER_STATISTICS_PACKAGE for this connection. *
  • BEGIN [TRANSACTION]: Begins a new transaction. This statement is optional when * the connection is not in autocommit mode, as a new transaction will automatically be * started when a query or update statement is issued. In autocommit mode, this statement will @@ -448,6 +453,29 @@ public interface Connection extends AutoCloseable { */ String getOptimizerVersion(); + /** + * Sets the query optimizer statistics package + * + * @param optimizerStatisticsPackage The query optimizer statistics package to use. Must be a + * string composed of letters, numbers, dashes and underscores or an empty string. The empty + * string will instruct the connection to use the optimizer statistics package that is defined + * the environment variable SPANNER_OPTIMIZER_STATISTICS_PACKAGE. If no value is + * specified in the environment variable, the client level query optimizer is used. If none is + * set, the default query optimizer of Cloud Spanner is used. + */ + default void setOptimizerStatisticsPackage(String optimizerStatisticsPackage) { + throw new UnsupportedOperationException("Unimplemented"); + } + + /** + * Gets the current query optimizer statistics package of this connection. + * + * @return The query optimizer statistics package that is currently used by this connection. + */ + default String getOptimizerStatisticsPackage() { + throw new UnsupportedOperationException("Unimplemented"); + } + /** * Sets whether this connection should request commit statistics from Cloud Spanner for read/write * transactions and DML statements in autocommit mode. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java index 345c881d29..ecf79a8086 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java @@ -433,6 +433,20 @@ public String getOptimizerVersion() { return this.queryOptions.getOptimizerVersion(); } + @Override + public void setOptimizerStatisticsPackage(String optimizerStatisticsPackage) { + Preconditions.checkNotNull(optimizerStatisticsPackage); + ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG); + this.queryOptions = + queryOptions.toBuilder().setOptimizerStatisticsPackage(optimizerStatisticsPackage).build(); + } + + @Override + public String getOptimizerStatisticsPackage() { + ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG); + return this.queryOptions.getOptimizerStatisticsPackage(); + } + @Override public void setStatementTimeout(long timeout, TimeUnit unit) { Preconditions.checkArgument(timeout > 0L, "Zero or negative timeout values are not allowed"); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java index f5ccaddf6b..3a1f321d09 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java @@ -157,6 +157,7 @@ public String[] getValidValues() { private static final String DEFAULT_NUM_CHANNELS = null; private static final String DEFAULT_USER_AGENT = null; private static final String DEFAULT_OPTIMIZER_VERSION = ""; + private static final String DEFAULT_OPTIMIZER_STATISTICS_PACKAGE = ""; private static final boolean DEFAULT_RETURN_COMMIT_STATS = false; private static final boolean DEFAULT_LENIENT = false; @@ -190,6 +191,9 @@ public String[] getValidValues() { private static final String USER_AGENT_PROPERTY_NAME = "userAgent"; /** Query optimizer version to use for a connection. */ private static final String OPTIMIZER_VERSION_PROPERTY_NAME = "optimizerVersion"; + /** Query optimizer statistics package to use for a connection. */ + private static final String OPTIMIZER_STATISTICS_PACKAGE_PROPERTY_NAME = + "optimizerStatisticsPackage"; /** Name of the 'lenientMode' connection property. */ public static final String LENIENT_PROPERTY_NAME = "lenient"; @@ -238,6 +242,8 @@ public String[] getValidValues() { ConnectionProperty.createStringProperty( OPTIMIZER_VERSION_PROPERTY_NAME, "Sets the default query optimizer version to use for this connection."), + ConnectionProperty.createStringProperty( + OPTIMIZER_STATISTICS_PACKAGE_PROPERTY_NAME, ""), ConnectionProperty.createBooleanProperty("returnCommitStats", "", false), ConnectionProperty.createBooleanProperty( "autoConfigEmulator", @@ -521,6 +527,7 @@ private ConnectionOptions(Builder builder) { this.userAgent = parseUserAgent(this.uri); QueryOptions.Builder queryOptionsBuilder = QueryOptions.newBuilder(); queryOptionsBuilder.setOptimizerVersion(parseOptimizerVersion(this.uri)); + queryOptionsBuilder.setOptimizerStatisticsPackage(parseOptimizerStatisticsPackage(this.uri)); this.queryOptions = queryOptionsBuilder.build(); this.returnCommitStats = parseReturnCommitStats(this.uri); this.autoConfigEmulator = parseAutoConfigEmulator(this.uri); @@ -695,6 +702,12 @@ static String parseOptimizerVersion(String uri) { return value != null ? value : DEFAULT_OPTIMIZER_VERSION; } + @VisibleForTesting + static String parseOptimizerStatisticsPackage(String uri) { + String value = parseUriProperty(uri, OPTIMIZER_STATISTICS_PACKAGE_PROPERTY_NAME); + return value != null ? value : DEFAULT_OPTIMIZER_STATISTICS_PACKAGE; + } + @VisibleForTesting static boolean parseReturnCommitStats(String uri) { String value = parseUriProperty(uri, "returnCommitStats"); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java index 5cbbdfb7c4..3f6dba9a40 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutor.java @@ -66,6 +66,10 @@ interface ConnectionStatementExecutor { StatementResult statementShowOptimizerVersion(); + StatementResult statementSetOptimizerStatisticsPackage(String optimizerStatisticsPackage); + + StatementResult statementShowOptimizerStatisticsPackage(); + StatementResult statementSetReturnCommitStats(Boolean returnCommitStats); StatementResult statementShowReturnCommitStats(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java index 1d7a202c5e..0f3f7470a6 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorImpl.java @@ -23,6 +23,7 @@ import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.RUN_BATCH; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_AUTOCOMMIT; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_AUTOCOMMIT_DML_MODE; +import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_OPTIMIZER_STATISTICS_PACKAGE; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_OPTIMIZER_VERSION; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_READONLY; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SET_READ_ONLY_STALENESS; @@ -34,6 +35,7 @@ import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_AUTOCOMMIT_DML_MODE; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_COMMIT_RESPONSE; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_COMMIT_TIMESTAMP; +import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_OPTIMIZER_STATISTICS_PACKAGE; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_OPTIMIZER_VERSION; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_READONLY; import static com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType.SHOW_READ_ONLY_STALENESS; @@ -230,6 +232,20 @@ public StatementResult statementShowOptimizerVersion() { "OPTIMIZER_VERSION", getConnection().getOptimizerVersion(), SHOW_OPTIMIZER_VERSION); } + @Override + public StatementResult statementSetOptimizerStatisticsPackage(String optimizerStatisticsPackage) { + getConnection().setOptimizerStatisticsPackage(optimizerStatisticsPackage); + return noResult(SET_OPTIMIZER_STATISTICS_PACKAGE); + } + + @Override + public StatementResult statementShowOptimizerStatisticsPackage() { + return resultSet( + "OPTIMIZER_STATISTICS_PACKAGE", + getConnection().getOptimizerStatisticsPackage(), + SHOW_OPTIMIZER_STATISTICS_PACKAGE); + } + @Override public StatementResult statementSetReturnCommitStats(Boolean returnCommitStats) { getConnection().setReturnCommitStats(returnCommitStats); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java index 94269d6ac7..5ece3a20e9 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/StatementResult.java @@ -65,6 +65,8 @@ enum ClientSideStatementType { SET_READ_ONLY_STALENESS, SHOW_OPTIMIZER_VERSION, SET_OPTIMIZER_VERSION, + SHOW_OPTIMIZER_STATISTICS_PACKAGE, + SET_OPTIMIZER_STATISTICS_PACKAGE, SHOW_RETURN_COMMIT_STATS, SET_RETURN_COMMIT_STATS, BEGIN, diff --git a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json index 6761374ea5..4dfe480395 100644 --- a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json +++ b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json @@ -76,6 +76,14 @@ "method": "statementShowOptimizerVersion", "exampleStatements": ["show variable optimizer_version"] }, + { + "name": "SHOW VARIABLE OPTIMIZER_STATISTICS_PACKAGE", + "executorName": "ClientSideStatementNoParamExecutor", + "resultType": "RESULT_SET", + "regex": "(?is)\\A\\s*show\\s+variable\\s+optimizer_statistics_package\\s*\\z", + "method": "statementShowOptimizerStatisticsPackage", + "exampleStatements": ["show variable optimizer_statistics_package"] + }, { "name": "SHOW VARIABLE RETURN_COMMIT_STATS", "executorName": "ClientSideStatementNoParamExecutor", @@ -281,6 +289,20 @@ "converterName": "ClientSideStatementValueConverters$StringValueConverter" } }, + { + "name": "SET OPTIMIZER_STATISTICS_PACKAGE = ''|''", + "executorName": "ClientSideStatementSetExecutor", + "resultType": "NO_RESULT", + "regex": "(?is)\\A\\s*set\\s+optimizer_statistics_package\\s*(?:=)\\s*(.*)\\z", + "method": "statementSetOptimizerStatisticsPackage", + "exampleStatements": ["set optimizer_statistics_package='auto_20191128_14_47_22UTC'", "set optimizer_statistics_package=''"], + "setStatement": { + "propertyName": "OPTIMIZER_STATISTICS_PACKAGE", + "separator": "=", + "allowedValues": "'((\\S+)|())'", + "converterName": "ClientSideStatementValueConverters$StringValueConverter" + } + }, { "name": "SET RETURN_COMMIT_STATS = TRUE|FALSE", "executorName": "ClientSideStatementSetExecutor", @@ -296,4 +318,4 @@ } } ] -} \ No newline at end of file +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractReadContextTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractReadContextTest.java index 16f5dc8a25..66c3609d6f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractReadContextTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractReadContextTest.java @@ -52,7 +52,12 @@ public static Collection parameters() { List params = new ArrayList<>(); params.add(new Object[] {QueryOptions.getDefaultInstance()}); params.add( - new Object[] {QueryOptions.newBuilder().setOptimizerVersion("some-version").build()}); + new Object[] { + QueryOptions.newBuilder() + .setOptimizerVersion("some-version") + .setOptimizerStatisticsPackage("some-package") + .build() + }); return params; } @@ -134,7 +139,11 @@ public void executeSqlRequestBuilderWithQueryOptions() { context .getExecuteSqlRequestBuilder( Statement.newBuilder("SELECT FOO FROM BAR") - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("2.0").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("2.0") + .setOptimizerStatisticsPackage("custom-package") + .build()) .build(), QueryMode.NORMAL, Options.fromQueryOptions(), @@ -142,6 +151,8 @@ public void executeSqlRequestBuilderWithQueryOptions() { .build(); assertThat(request.getSql()).isEqualTo("SELECT FOO FROM BAR"); assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("2.0"); + assertThat(request.getQueryOptions().getOptimizerStatisticsPackage()) + .isEqualTo("custom-package"); } @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java index 42c7bb1b94..0a1efc2a48 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java @@ -1529,18 +1529,25 @@ public void testBackendQueryOptions() { .singleUse() .executeQuery( Statement.newBuilder(SELECT1.getSql()) - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("1").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()) .build())) { // Just iterate over the results to execute the query. while (rs.next()) {} } - // Check that the last query was executed using a custom optimizer version. + // Check that the last query was executed using a custom optimizer version and statistics + // package. List requests = mockSpanner.getRequests(); assertThat(requests).isNotEmpty(); assertThat(requests.get(requests.size() - 1)).isInstanceOf(ExecuteSqlRequest.class); ExecuteSqlRequest request = (ExecuteSqlRequest) requests.get(requests.size() - 1); assertThat(request.getQueryOptions()).isNotNull(); assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("1"); + assertThat(request.getQueryOptions().getOptimizerStatisticsPackage()) + .isEqualTo("custom-package"); } } @@ -1562,20 +1569,27 @@ public void testBackendQueryOptionsWithAnalyzeQuery() { try (ResultSet rs = tx.analyzeQuery( Statement.newBuilder(SELECT1.getSql()) - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("1").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()) .build(), QueryAnalyzeMode.PROFILE)) { // Just iterate over the results to execute the query. while (rs.next()) {} } } - // Check that the last query was executed using a custom optimizer version. + // Check that the last query was executed using a custom optimizer version and statistics + // package. List requests = mockSpanner.getRequests(); assertThat(requests).isNotEmpty(); assertThat(requests.get(requests.size() - 1)).isInstanceOf(ExecuteSqlRequest.class); ExecuteSqlRequest request = (ExecuteSqlRequest) requests.get(requests.size() - 1); assertThat(request.getQueryOptions()).isNotNull(); assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("1"); + assertThat(request.getQueryOptions().getOptimizerStatisticsPackage()) + .isEqualTo("custom-package"); assertThat(request.getQueryMode()).isEqualTo(QueryMode.PROFILE); } } @@ -1600,19 +1614,26 @@ public void testBackendPartitionQueryOptions() { transaction.partitionQuery( PartitionOptions.newBuilder().setMaxPartitions(10L).build(), Statement.newBuilder(SELECT1.getSql()) - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("1").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()) .build()); try (ResultSet rs = transaction.execute(partitions.get(0))) { // Just iterate over the results to execute the query. while (rs.next()) {} } - // Check that the last query was executed using a custom optimizer version. + // Check that the last query was executed using a custom optimizer version and statistics + // package. List requests = mockSpanner.getRequests(); assertThat(requests).isNotEmpty(); assertThat(requests.get(requests.size() - 1)).isInstanceOf(ExecuteSqlRequest.class); ExecuteSqlRequest request = (ExecuteSqlRequest) requests.get(requests.size() - 1); assertThat(request.getQueryOptions()).isNotNull(); assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("1"); + assertThat(request.getQueryOptions().getOptimizerStatisticsPackage()) + .isEqualTo("custom-package"); } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerImplTest.java index c8a93d64ee..0cc0e6e9fb 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerImplTest.java @@ -96,7 +96,11 @@ public void getDbclientAgainGivesSame() { @Test public void queryOptions() { - QueryOptions queryOptions = QueryOptions.newBuilder().setOptimizerVersion("2").build(); + QueryOptions queryOptions = + QueryOptions.newBuilder() + .setOptimizerVersion("2") + .setOptimizerStatisticsPackage("custom-package") + .build(); QueryOptions defaultOptions = QueryOptions.getDefaultInstance(); DatabaseId db = DatabaseId.of("p", "i", "d"); DatabaseId otherDb = DatabaseId.of("p", "i", "other"); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java index 74ce8d7cb0..3698d3379f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java @@ -54,6 +54,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.Nonnull; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -556,36 +557,80 @@ public void testSetEmulatorHostWithProtocol() { @Test public void testDefaultQueryOptions() { - SpannerOptions.useEnvironment(() -> ""); + SpannerOptions.useEnvironment( + new SpannerOptions.SpannerEnvironment() { + @Override + public String getOptimizerVersion() { + return ""; + } + + @Nonnull + @Override + public String getOptimizerStatisticsPackage() { + return ""; + } + }); SpannerOptions options = SpannerOptions.newBuilder() .setDefaultQueryOptions( DatabaseId.of("p", "i", "d"), - QueryOptions.newBuilder().setOptimizerVersion("1").build()) + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()) .setProjectId("p") .setCredentials(NoCredentials.getInstance()) .build(); assertThat(options.getDefaultQueryOptions(DatabaseId.of("p", "i", "d"))) - .isEqualTo(QueryOptions.newBuilder().setOptimizerVersion("1").build()); + .isEqualTo( + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()); assertThat(options.getDefaultQueryOptions(DatabaseId.of("p", "i", "o"))) .isEqualTo(QueryOptions.getDefaultInstance()); - // Now simulate that the user has set an environment variable for the query optimizer version. - SpannerOptions.useEnvironment(() -> "2"); - // Create options with '1' as the default query optimizer version. This should be overridden by + // Now simulate that the user has set an environment variable for the query optimizer version + // and statistics package. + SpannerOptions.useEnvironment( + new SpannerOptions.SpannerEnvironment() { + @Override + public String getOptimizerVersion() { + return "2"; + } + + @Nonnull + @Override + public String getOptimizerStatisticsPackage() { + return "env-package"; + } + }); + // Create options with '1' as the default query optimizer version and 'custom-package' as the + // default query optimizer statistics package. These values should be overridden by // the environment variable. options = SpannerOptions.newBuilder() .setDefaultQueryOptions( DatabaseId.of("p", "i", "d"), - QueryOptions.newBuilder().setOptimizerVersion("1").build()) + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()) .setProjectId("p") .setCredentials(NoCredentials.getInstance()) .build(); assertThat(options.getDefaultQueryOptions(DatabaseId.of("p", "i", "d"))) - .isEqualTo(QueryOptions.newBuilder().setOptimizerVersion("2").build()); + .isEqualTo( + QueryOptions.newBuilder() + .setOptimizerVersion("2") + .setOptimizerStatisticsPackage("env-package") + .build()); assertThat(options.getDefaultQueryOptions(DatabaseId.of("p", "i", "o"))) - .isEqualTo(QueryOptions.newBuilder().setOptimizerVersion("2").build()); + .isEqualTo( + QueryOptions.newBuilder() + .setOptimizerVersion("2") + .setOptimizerStatisticsPackage("env-package") + .build()); } @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractConnectionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractConnectionImplTest.java index 7cfc4dd914..ca3d6313d7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractConnectionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractConnectionImplTest.java @@ -630,6 +630,51 @@ public void testGetOptimizerVersion() { } } + boolean isSetOptimizerStatisticsPackageAllowed() { + return !getConnection().isClosed(); + } + + @Test + public void testSetOptimizerStatisticsPackage() { + try (Connection connection = getConnection()) { + if (isSetOptimizerStatisticsPackageAllowed()) { + for (String statisticsPackage : new String[] {"custom-package", ""}) { + log("SET OPTIMIZER_STATISTICS_PACKAGE='" + statisticsPackage + "';"); + connection.setOptimizerStatisticsPackage(statisticsPackage); + + log("@EXPECT RESULT_SET 'OPTIMIZER_STATISTICS_PACKAGE','" + statisticsPackage + "'"); + log("SHOW VARIABLE OPTIMIZER_STATISTICS_PACKAGE;"); + assertThat(connection.getOptimizerStatisticsPackage(), is(equalTo(statisticsPackage))); + } + } else { + log("@EXPECT EXCEPTION FAILED_PRECONDITION"); + log("SET OPTIMIZER_STATISTICS_PACKAGE='custom-package';"); + exception.expect(matchCode(ErrorCode.FAILED_PRECONDITION)); + connection.setOptimizerStatisticsPackage("custom-package"); + } + } + } + + boolean isGetOptimizerStatisticsPackageAllowed() { + return !getConnection().isClosed(); + } + + @Test + public void testGetOptimizerStatisticsPackage() { + try (Connection connection = getConnection()) { + if (isGetOptimizerStatisticsPackageAllowed()) { + log("@EXPECT RESULT_SET 'OPTIMIZER_STATISTICS_PACKAGE'"); + log("SHOW VARIABLE OPTIMIZER_STATISTICS_PACKAGE;"); + assertThat(connection.getOptimizerStatisticsPackage(), is(notNullValue())); + } else { + log("@EXPECT EXCEPTION FAILED_PRECONDITION"); + log("SHOW VARIABLE OPTIMIZER_STATISTICS_PACKAGE;"); + exception.expect(matchCode(ErrorCode.FAILED_PRECONDITION)); + connection.getOptimizerStatisticsPackage(); + } + } + } + abstract boolean isCommitAllowed(); @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java index a24231030b..8e5ed22454 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java @@ -607,6 +607,72 @@ public void testExecuteGetOptimizerVersion() { } } + @Test + public void testExecuteSetOptimizerStatisticsPackage() { + try (ConnectionImpl subject = + createConnection( + ConnectionOptions.newBuilder() + .setCredentials(NoCredentials.getInstance()) + .setUri(URI) + .build())) { + assertThat(subject.getOptimizerStatisticsPackage(), is(equalTo(""))); + + StatementResult res = + subject.execute(Statement.of("set optimizer_statistics_package='custom-package'")); + assertThat(res.getResultType(), is(equalTo(ResultType.NO_RESULT))); + assertThat(subject.getOptimizerStatisticsPackage(), is(equalTo("custom-package"))); + + res = subject.execute(Statement.of("set optimizer_statistics_package=''")); + assertThat(res.getResultType(), is(equalTo(ResultType.NO_RESULT))); + assertThat(subject.getOptimizerStatisticsPackage(), is(equalTo(""))); + } + } + + @Test + public void testExecuteSetOptimizerStatisticsPackageInvalidValue() { + try (ConnectionImpl subject = + createConnection( + ConnectionOptions.newBuilder() + .setCredentials(NoCredentials.getInstance()) + .setUri(URI) + .build())) { + assertThat(subject.getOptimizerVersion(), is(equalTo(""))); + + try { + subject.execute(Statement.of("set optimizer_statistics_package=' '")); + fail("Missing expected exception"); + } catch (SpannerException e) { + assertThat(e.getErrorCode(), is(equalTo(ErrorCode.INVALID_ARGUMENT))); + } + } + } + + @Test + public void testExecuteGetOptimizerStatisticsPackage() { + try (ConnectionImpl subject = + createConnection( + ConnectionOptions.newBuilder() + .setCredentials(NoCredentials.getInstance()) + .setUri(URI) + .build())) { + assertThat(subject.getOptimizerStatisticsPackage(), is(equalTo(""))); + + StatementResult res = + subject.execute(Statement.of("show variable optimizer_statistics_package")); + assertThat(res.getResultType(), is(equalTo(ResultType.RESULT_SET))); + assertThat(res.getResultSet().next(), is(true)); + assertThat(res.getResultSet().getString("OPTIMIZER_STATISTICS_PACKAGE"), is(equalTo(""))); + + subject.execute(Statement.of("set optimizer_statistics_package='custom-package'")); + res = subject.execute(Statement.of("show variable optimizer_statistics_package")); + assertThat(res.getResultType(), is(equalTo(ResultType.RESULT_SET))); + assertThat(res.getResultSet().next(), is(true)); + assertThat( + res.getResultSet().getString("OPTIMIZER_STATISTICS_PACKAGE"), + is(equalTo("custom-package"))); + } + } + @Test public void testExecuteSetReturnCommitStats() { try (ConnectionImpl subject = @@ -1297,55 +1363,81 @@ UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork() { return unitOfWork; } }) { - // Execute query with an optimizer version set on the connection. + // Execute query with an optimizer version and statistics package set on the connection. impl.setOptimizerVersion("1"); + impl.setOptimizerStatisticsPackage("custom-package-1"); impl.executeQuery(Statement.of("SELECT FOO FROM BAR")); verify(unitOfWork) .executeQueryAsync( StatementParser.INSTANCE.parse( Statement.newBuilder("SELECT FOO FROM BAR") - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("1").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package-1") + .build()) .build()), AnalyzeMode.NONE); - // Execute query with an optimizer version set on the connection. + // Execute query with an optimizer version and statistics package set on the connection. impl.setOptimizerVersion("2"); + impl.setOptimizerStatisticsPackage("custom-package-2"); impl.executeQuery(Statement.of("SELECT FOO FROM BAR")); verify(unitOfWork) .executeQueryAsync( StatementParser.INSTANCE.parse( Statement.newBuilder("SELECT FOO FROM BAR") - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("2").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("2") + .setOptimizerStatisticsPackage("custom-package-2") + .build()) .build()), AnalyzeMode.NONE); - // Execute query with an optimizer version set on the connection and PrefetchChunks query + // Execute query with an optimizer version and statistics package set on the connection and + // PrefetchChunks query // option specified for the query. QueryOption prefetchOption = Options.prefetchChunks(100); impl.setOptimizerVersion("3"); + impl.setOptimizerStatisticsPackage("custom-package-3"); impl.executeQuery(Statement.of("SELECT FOO FROM BAR"), prefetchOption); verify(unitOfWork) .executeQueryAsync( StatementParser.INSTANCE.parse( Statement.newBuilder("SELECT FOO FROM BAR") - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("3").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("3") + .setOptimizerStatisticsPackage("custom-package-3") + .build()) .build()), AnalyzeMode.NONE, prefetchOption); - // Execute query with an optimizer version set on the connection, and the same options also + // Execute query with an optimizer version and statistics package set on the connection, and + // the same options also // passed in to the query. The specific options passed in to the query should take precedence. impl.setOptimizerVersion("4"); + impl.setOptimizerStatisticsPackage("custom-package-4"); impl.executeQuery( Statement.newBuilder("SELECT FOO FROM BAR") - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("5").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("5") + .setOptimizerStatisticsPackage("custom-package-5") + .build()) .build(), prefetchOption); verify(unitOfWork) .executeQueryAsync( StatementParser.INSTANCE.parse( Statement.newBuilder("SELECT FOO FROM BAR") - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("5").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("5") + .setOptimizerStatisticsPackage("custom-package-5") + .build()) .build()), AnalyzeMode.NONE, prefetchOption); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorTest.java index d92b8cf265..41a87d31b9 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementExecutorTest.java @@ -101,6 +101,12 @@ public void testStatementGetOptimizerVersion() { verify(connection).getOptimizerVersion(); } + @Test + public void testStatementGetOptimizerStatisticsPackage() { + subject.statementShowOptimizerStatisticsPackage(); + verify(connection).getOptimizerStatisticsPackage(); + } + @Test public void testStatementGetReadTimestamp() { subject.statementShowReadTimestamp(); @@ -183,6 +189,14 @@ public void testStatementSetOptimizerVersion() { verify(connection).setOptimizerVersion("LATEST"); } + @Test + public void testStatementSetOptimizerStatisticsPackage() { + subject.statementSetOptimizerStatisticsPackage("custom-package"); + verify(connection).setOptimizerStatisticsPackage("custom-package"); + subject.statementSetOptimizerStatisticsPackage(""); + verify(connection).setOptimizerStatisticsPackage(""); + } + @Test public void testStatementSetStatementTimeout() { subject.statementSetStatementTimeout(Duration.newBuilder().setNanos(100).build()); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithNoParametersTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithNoParametersTest.java index e3be3cfeae..84c6b8c8ce 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithNoParametersTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithNoParametersTest.java @@ -126,6 +126,21 @@ public void testExecuteGetOptimizerVersion() { verify(connection, times(1)).getOptimizerVersion(); } + @Test + public void testExecuteGetOptimizerStatisticsPackage() { + ParsedStatement statement = + parser.parse(Statement.of("show variable optimizer_statistics_package")); + ConnectionImpl connection = mock(ConnectionImpl.class); + ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); + when(executor.getConnection()).thenReturn(connection); + when(executor.statementShowOptimizerStatisticsPackage()).thenCallRealMethod(); + when(connection.getOptimizerStatisticsPackage()).thenReturn("custom-package"); + statement + .getClientSideStatement() + .execute(executor, "show variable optimizer_statistics_package"); + verify(connection, times(1)).getOptimizerStatisticsPackage(); + } + @Test public void testExecuteBegin() { ParsedStatement subject = parser.parse(Statement.of("begin")); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithOneParameterTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithOneParameterTest.java index d5d4ec34d2..cfb61154af 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithOneParameterTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionStatementWithOneParameterTest.java @@ -160,6 +160,22 @@ public void testExecuteSetOptimizerVersion() { } } + @Test + public void testExecuteSetOptimizerStatisticsPackage() { + ParsedStatement subject = parser.parse(Statement.of("set optimizer_statistics_package='foo'")); + ConnectionImpl connection = mock(ConnectionImpl.class); + ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class); + when(executor.getConnection()).thenReturn(connection); + when(executor.statementSetOptimizerStatisticsPackage(any(String.class))).thenCallRealMethod(); + for (String statisticsPackage : new String[] {"custom-package", ""}) { + subject + .getClientSideStatement() + .execute( + executor, String.format("set optimizer_statistics_package='%s'", statisticsPackage)); + verify(connection, times(1)).setOptimizerStatisticsPackage(statisticsPackage); + } + } + @Test public void testExecuteSetTransaction() { ParsedStatement subject = parser.parse(Statement.of("set transaction read_only")); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionTest.java index 38d182cc54..f21fbc7209 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionTest.java @@ -35,6 +35,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import javax.annotation.Nonnull; import org.junit.AfterClass; import org.junit.Test; import org.junit.experimental.runners.Enclosed; @@ -42,24 +43,24 @@ @RunWith(Enclosed.class) public class ConnectionTest { - - public static class DefaultConnectionOptionsTest extends AbstractMockServerTest { + public static class EnvironmentConnectionOptionsTest extends AbstractMockServerTest { @Test - public void testDefaultOptimizerVersion() { - try (Connection connection = createConnection()) { - try (ResultSet rs = - connection.executeQuery(Statement.of("SHOW VARIABLE OPTIMIZER_VERSION"))) { - assertThat(rs.next()).isTrue(); - assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo(""); - assertThat(rs.next()).isFalse(); - } - } - } - - @Test - public void testUseOptimizerVersionFromEnvironment() { + public void testUseOptimizerVersionAndStatisticsPackageFromEnvironment() { try { - SpannerOptions.useEnvironment(() -> "20"); + SpannerOptions.useEnvironment( + new SpannerOptions.SpannerEnvironment() { + @Nonnull + @Override + public String getOptimizerVersion() { + return "20"; + } + + @Nonnull + @Override + public String getOptimizerStatisticsPackage() { + return "env-package"; + } + }); try (Connection connection = createConnection()) { // Do a query and verify that the version from the environment is used. try (ResultSet rs = connection.executeQuery(SELECT_COUNT_STATEMENT)) { @@ -69,10 +70,13 @@ public void testUseOptimizerVersionFromEnvironment() { // Verify query options from the environment. ExecuteSqlRequest request = getLastExecuteSqlRequest(); assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("20"); + assertThat(request.getQueryOptions().getOptimizerStatisticsPackage()) + .isEqualTo("env-package"); } // Now set one of the query options on the connection. That option should be used in // combination with the other option from the environment. connection.execute(Statement.of("SET OPTIMIZER_VERSION='30'")); + connection.execute(Statement.of("SET OPTIMIZER_STATISTICS_PACKAGE='custom-package'")); try (ResultSet rs = connection.executeQuery(SELECT_COUNT_STATEMENT)) { assertThat(rs.next()).isTrue(); assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT); @@ -81,6 +85,9 @@ public void testUseOptimizerVersionFromEnvironment() { ExecuteSqlRequest request = getLastExecuteSqlRequest(); // Optimizer version should come from the connection. assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("30"); + // Optimizer statistics package should come from the connection. + assertThat(request.getQueryOptions().getOptimizerStatisticsPackage()) + .isEqualTo("custom-package"); } // Now specify options directly for the query. These should override both the environment // and what is set on the connection. @@ -90,6 +97,7 @@ public void testUseOptimizerVersionFromEnvironment() { .withQueryOptions( QueryOptions.newBuilder() .setOptimizerVersion("user-defined-version") + .setOptimizerStatisticsPackage("user-defined-statistics-package") .build()) .build())) { assertThat(rs.next()).isTrue(); @@ -100,12 +108,41 @@ public void testUseOptimizerVersionFromEnvironment() { // Optimizer version should come from the query. assertThat(request.getQueryOptions().getOptimizerVersion()) .isEqualTo("user-defined-version"); + // Optimizer statistics package should come from the query. + assertThat(request.getQueryOptions().getOptimizerStatisticsPackage()) + .isEqualTo("user-defined-statistics-package"); } } } finally { SpannerOptions.useDefaultEnvironment(); } } + } + + public static class DefaultConnectionOptionsTest extends AbstractMockServerTest { + @Test + public void testDefaultOptimizerVersion() { + try (Connection connection = createConnection()) { + try (ResultSet rs = + connection.executeQuery(Statement.of("SHOW VARIABLE OPTIMIZER_VERSION"))) { + assertThat(rs.next()).isTrue(); + assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo(""); + assertThat(rs.next()).isFalse(); + } + } + } + + @Test + public void testDefaultOptimizerStatisticsPackage() { + try (Connection connection = createConnection()) { + try (ResultSet rs = + connection.executeQuery(Statement.of("SHOW VARIABLE OPTIMIZER_STATISTICS_PACKAGE"))) { + assertThat(rs.next()).isTrue(); + assertThat(rs.getString("OPTIMIZER_STATISTICS_PACKAGE")).isEqualTo(""); + assertThat(rs.next()).isFalse(); + } + } + } @Test public void testExecuteInvalidBatchUpdate() { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITQueryOptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITQueryOptionsTest.java new file mode 100644 index 0000000000..09f4ada43a --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITQueryOptionsTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.connection.it; + +import com.google.cloud.spanner.connection.ITAbstractSpannerTest; +import com.google.cloud.spanner.connection.SqlScriptVerifier; +import com.google.cloud.spanner.connection.SqlScriptVerifier.SpannerGenericConnection; +import org.junit.Before; +import org.junit.Test; + +public class ITQueryOptionsTest extends ITAbstractSpannerTest { + + private static final String TEST_QUERY_OPTIONS = "ITSqlScriptTest_TestQueryOptions.sql"; + + private SqlScriptVerifier verifier; + + @Before + public void setUp() { + verifier = new SqlScriptVerifier(); + } + + @Test + public void verifiesQueryOptions() throws Exception { + try (ITConnection connection = createConnection()) { + verifier.verifyStatementsInFile( + SpannerGenericConnection.of(connection), TEST_QUERY_OPTIONS, SqlScriptVerifier.class); + } + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryOptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryOptionsTest.java index 1ff035fca8..57aa77e6a7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryOptionsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryOptionsTest.java @@ -59,12 +59,17 @@ public static void setUpDatabase() { @Test public void executeQuery() { // Version '1' should work. + // Statistics package 'custom-package' should work. try (ResultSet rs = client .singleUse() .executeQuery( Statement.newBuilder("SELECT 1") - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("1").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()) .build())) { while (rs.next()) { assertThat(rs.getLong(0)).isEqualTo(1L); @@ -104,6 +109,7 @@ public void executeQuery() { @Test public void executeUpdate() { // Optimizer version 1 should work. + // Optimizer statistics package 'custom-package' should work. assertThat( client .readWriteTransaction() @@ -116,7 +122,10 @@ public void executeUpdate() { .bind("name") .to("One") .withQueryOptions( - QueryOptions.newBuilder().setOptimizerVersion("1").build()) + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()) .build()))) .isEqualTo(1L); @@ -158,11 +167,15 @@ public void executeUpdate() { assertThat(e.getMessage()).contains("Query optimizer version: 100000 is not supported"); } - // Setting an optimizer version for PDML should also be allowed. + // Setting an optimizer version and statistics package for PDML should also be allowed. assertThat( client.executePartitionedUpdate( Statement.newBuilder("UPDATE TEST SET NAME='updated' WHERE 1=1") - .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("1").build()) + .withQueryOptions( + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()) .build())) .isEqualTo(2L); } @@ -170,12 +183,17 @@ public void executeUpdate() { @Test public void spannerOptions() { // Version '1' should work. + // Statistics package 'custom-package' should work. try (Spanner spanner = env.getTestHelper() .getOptions() .toBuilder() .setDefaultQueryOptions( - db.getId(), QueryOptions.newBuilder().setOptimizerVersion("1").build()) + db.getId(), + QueryOptions.newBuilder() + .setOptimizerVersion("1") + .setOptimizerStatisticsPackage("custom-package") + .build()) .build() .getService()) { DatabaseClient client = spanner.getDatabaseClient(db.getId()); diff --git a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ITSqlScriptTest_TestQueryOptions.sql b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ITSqlScriptTest_TestQueryOptions.sql index 923cc97167..7b1449cdf9 100644 --- a/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ITSqlScriptTest_TestQueryOptions.sql +++ b/google-cloud-spanner/src/test/resources/com/google/cloud/spanner/connection/ITSqlScriptTest_TestQueryOptions.sql @@ -15,10 +15,13 @@ */ /* - * Test setting and getting the optimizer version to use. + * Test setting and getting the following query options: + * - Optimizer version + * - Optimizer statistics package */ -- Set and get valid values. +-- Optimizer version @EXPECT NO_RESULT SET OPTIMIZER_VERSION = '1'; @@ -43,9 +46,27 @@ SET OPTIMIZER_VERSION = ''; @EXPECT RESULT_SET 'OPTIMIZER_VERSION','' SHOW VARIABLE OPTIMIZER_VERSION; +-- Optimizer statistics package +@EXPECT NO_RESULT +SET OPTIMIZER_STATISTICS_PACKAGE = 'custom-package_withNumbers-1234'; + +@EXPECT RESULT_SET 'OPTIMIZER_STATISTICS_PACKAGE','custom-package_withNumbers-1234' +SHOW VARIABLE OPTIMIZER_STATISTICS_PACKAGE; + +@EXPECT NO_RESULT +SET OPTIMIZER_STATISTICS_PACKAGE = ''; + +@EXPECT RESULT_SET 'OPTIMIZER_STATISTICS_PACKAGE','' +SHOW VARIABLE OPTIMIZER_STATISTICS_PACKAGE; + -- Try to set invalid values. +-- Optimizer version @EXPECT EXCEPTION INVALID_ARGUMENT 'INVALID_ARGUMENT: Unknown value for OPTIMIZER_VERSION: 'None'' SET OPTIMIZER_VERSION = 'None'; @EXPECT EXCEPTION INVALID_ARGUMENT 'INVALID_ARGUMENT: Unknown value for OPTIMIZER_VERSION: 'v1'' SET OPTIMIZER_VERSION = 'v1'; + +-- Optimizer statistics package +@EXPECT EXCEPTION INVALID_ARGUMENT 'INVALID_ARGUMENT: Unknown value for OPTIMIZER_STATISTICS_PACKAGE: ' '' +SET OPTIMIZER_STATISTICS_PACKAGE = ' ';