Repository: googleapis/java-spanner-jdbc
Pull request #95 (2020-03-20 16:21:51)

deps: update spanner.version to v1.52.0
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1 | minor | `1.51.0` -> `1.52.0` |
| com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1 | minor | `1.51.0` -> `1.52.0` |
| com.google.api.grpc:grpc-google-cloud-spanner-v1 | minor | `1.51.0` -> `1.52.0` |
| [com.google.cloud:google-cloud-spanner](https://togithub.com/googleapis/java-spanner) | minor | `1.51.0` -> `1.52.0` |
| com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1 | minor | `1.51.0` -> `1.52.0` |
| com.google.api.grpc:proto-google-cloud-spanner-v1 | minor | `1.51.0` -> `1.52.0` |

---

### Release Notes

<details>
<summary>googleapis/java-spanner</summary>

### [`v1.52.0`](https://togithub.com/googleapis/java-spanner/blob/master/CHANGELOG.md#&#8203;1520httpswwwgithubcomgoogleapisjava-spannercomparev1510v1520-2020-03-20)

[Compare Source](https://togithub.com/googleapis/java-spanner/compare/v1.51.0...v1.52.0)

##### Features

-   add backup support ([#&#8203;100](https://www.github.com/googleapis/java-spanner/issues/100)) ([ed3874a](https://www.github.com/googleapis/java-spanner/commit/ed3874afcf55fe7381354e03dab3a3b97d7eb520))
-   add Backups protos and APIs ([#&#8203;97](https://www.github.com/googleapis/java-spanner/issues/97)) ([5643c22](https://www.github.com/googleapis/java-spanner/commit/5643c22a4531dac75b9fac5b128eb714a27920a0))

##### Bug Fixes

-   add client id to metrics to avoid collisions ([#&#8203;117](https://www.github.com/googleapis/java-spanner/issues/117)) ([338e136](https://www.github.com/googleapis/java-spanner/commit/338e136508edc6745f9371e8a5d66638021bc8d7)), closes [#&#8203;106](https://www.github.com/googleapis/java-spanner/issues/106)
-   ignore added interface methods for generated code ([#&#8203;101](https://www.github.com/googleapis/java-spanner/issues/101)) ([402cfa1](https://www.github.com/googleapis/java-spanner/commit/402cfa1e1e2994f7bb1b783cf823021b54fb175e)), closes [#&#8203;99](https://www.github.com/googleapis/java-spanner/issues/99)
-   use grpc 1.27.2 to prevent version conflicts ([#&#8203;105](https://www.github.com/googleapis/java-spanner/issues/105)) ([37b7c88](https://www.github.com/googleapis/java-spanner/commit/37b7c8859e5f35d85bd14ef72662614fd185c020))

##### Dependencies

-   update core dependencies ([#&#8203;94](https://www.github.com/googleapis/java-spanner/issues/94)) ([f3ca4c9](https://www.github.com/googleapis/java-spanner/commit/f3ca4c99c3d54f64c5eda11e4a4c076140fdbc6a))
-   update opencensus.version to v0.26.0 ([#&#8203;116](https://www.github.com/googleapis/java-spanner/issues/116)) ([1b8db0b](https://www.github.com/googleapis/java-spanner/commit/1b8db0b407429e02bb1e4c9af839afeed21dac5d))

</details>

---

### Renovate configuration

:date: **Schedule**: At any time (no schedule defined).

:vertical_traffic_light: **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

:recycle: **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

:no_bell: **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#googleapis/java-spanner-jdbc).

codecov[bot]: # [Codecov](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/95?src=pr&el=h1) Report
> Merging [#95](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/95?src=pr&el=desc) into [master](https://codecov.io/gh/googleapis/java-spanner-jdbc/commit/19eade25d480573401adf884d2efd2968e1ea6ae?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/95/graphs/tree.svg?width=650&token=ODlczvbGrj&height=150&src=pr)](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/95?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##             master      #95   +/-   ##
=========================================
  Coverage     77.79%   77.79%           
  Complexity     1723     1723           
=========================================
  Files            60       60           
  Lines          5836     5836           
  Branches        769      769           
=========================================
  Hits           4540     4540           
  Misses          923      923           
  Partials        373      373
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/95?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/95?src=pr&el=footer). Last update [19eade2...83405cf](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/95?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/pom.xml b/pom.xml
index 2691043..26a888b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,7 +58,7 @@
   </licenses>
   <properties>
     <site.installationModule>google-cloud-spanner-jdbc</site.installationModule>
-    <grpc.version>1.28.0</grpc.version>
+    <grpc.version>1.27.2</grpc.version>
     <api-client.version>1.30.9</api-client.version>
     <google.core.version>1.93.3</google.core.version>
     <gax.version>1.54.0</gax.version>
@@ -69,7 +69,7 @@
     <protobuf.version>3.11.4</protobuf.version>
     <threeten.version>1.4.1</threeten.version>
     <google.api-common.version>1.8.1</google.api-common.version>
-    <spanner.version>1.51.0</spanner.version>
+    <spanner.version>1.52.0</spanner.version>
     <hamcrest.version>2.2</hamcrest.version>
     <error-prone.version>2.3.4</error-prone.version>
     <google.auth.version>0.20.0</google.auth.version>
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/ClientSideStatementValueConverters.java b/src/main/java/com/google/cloud/spanner/jdbc/ClientSideStatementValueConverters.java
index ddeb394..92368fe 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/ClientSideStatementValueConverters.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/ClientSideStatementValueConverters.java
@@ -213,6 +213,20 @@ class ClientSideStatementValueConverters {
     }
   }
 
+  static class StringValueConverter implements ClientSideStatementValueConverter<String> {
+    public StringValueConverter(String allowedValues) {}
+
+    @Override
+    public Class<String> getParameterClass() {
+      return String.class;
+    }
+
+    @Override
+    public String convert(String value) {
+      return value;
+    }
+  }
+
   /** Converter for converting string values to {@link TransactionMode} values. */
   static class TransactionModeConverter
       implements ClientSideStatementValueConverter<TransactionMode> {
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/Connection.java b/src/main/java/com/google/cloud/spanner/jdbc/Connection.java
index 0ae8ed5..c3b32f8 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/Connection.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/Connection.java
@@ -78,6 +78,11 @@ import java.util.concurrent.TimeUnit;
  *   <li><code>
  *       SET READ_ONLY_STALENESS='STRONG' | 'MIN_READ_TIMESTAMP &lt;timestamp&gt;' | 'READ_TIMESTAMP &lt;timestamp&gt;' | 'MAX_STALENESS &lt;int64&gt;s|ms|mus|ns' | 'EXACT_STALENESS (&lt;int64&gt;s|ms|mus|ns)'
  *       </code>: Sets the value of <code>READ_ONLY_STALENESS</code> for this connection.
+ *   <li><code>SHOW OPTIMIZER_VERSION</code>: Returns the current value of <code>
+ *       OPTIMIZER_VERSION</code> of this connection as a {@link ResultSet}
+ *   <li><code>
+ *       SET OPTIMIZER_VERSION='&lt;version&gt;' | 'LATEST'
+ *       </code>: Sets the value of <code>OPTIMIZER_VERSION</code> for this connection.
  *   <li><code>BEGIN [TRANSACTION]</code>: 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
@@ -384,6 +389,24 @@ interface Connection extends AutoCloseable {
    */
   TimestampBound getReadOnlyStaleness();
 
+  /**
+   * Sets the query optimizer version to use for this connection.
+   *
+   * @param optimizerVersion The query optimizer version to use. Must be a valid optimizer version
+   *     number, the string <code>LATEST</code> or an empty string. The empty string will instruct
+   *     the connection to use the optimizer version that is defined in the environment variable
+   *     <code>SPANNER_OPTIMIZER_VERSION</code>. If no value is specified in the environment
+   *     variable, the default query optimizer of Cloud Spanner is used.
+   */
+  void setOptimizerVersion(String optimizerVersion);
+
+  /**
+   * Gets the current query optimizer version of this connection.
+   *
+   * @return The query optimizer version that is currently used by this connection.
+   */
+  String getOptimizerVersion();
+
   /**
    * Commits the current transaction of this connection. All mutations that have been buffered
    * during the current transaction will be written to the database.
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java
index 8490dfc..2a83fca 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java
@@ -35,6 +35,7 @@ import com.google.cloud.spanner.jdbc.StatementParser.StatementType;
 import com.google.cloud.spanner.jdbc.UnitOfWork.UnitOfWorkState;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
+import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -192,6 +193,7 @@ class ConnectionImpl implements Connection {
   private final List<TransactionRetryListener> transactionRetryListeners = new ArrayList<>();
   private AutocommitDmlMode autocommitDmlMode = AutocommitDmlMode.TRANSACTIONAL;
   private TimestampBound readOnlyStaleness = TimestampBound.strong();
+  private QueryOptions queryOptions = QueryOptions.getDefaultInstance();
 
   /** Create a connection and register it in the SpannerPool. */
   ConnectionImpl(ConnectionOptions options) {
@@ -204,6 +206,7 @@ class ConnectionImpl implements Connection {
     this.retryAbortsInternally = options.isRetryAbortsInternally();
     this.readOnly = options.isReadOnly();
     this.autocommit = options.isAutocommit();
+    this.queryOptions = this.queryOptions.toBuilder().mergeFrom(options.getQueryOptions()).build();
     this.ddlClient = createDdlClient();
     setDefaultTransactionOptions();
   }
@@ -389,6 +392,19 @@ class ConnectionImpl implements Connection {
     return this.readOnlyStaleness;
   }
 
+  @Override
+  public void setOptimizerVersion(String optimizerVersion) {
+    Preconditions.checkNotNull(optimizerVersion);
+    ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
+    this.queryOptions = queryOptions.toBuilder().setOptimizerVersion(optimizerVersion).build();
+  }
+
+  @Override
+  public String getOptimizerVersion() {
+    ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
+    return this.queryOptions.getOptimizerVersion();
+  }
+
   @Override
   public void setStatementTimeout(long timeout, TimeUnit unit) {
     Preconditions.checkArgument(timeout > 0L, "Zero or negative timeout values are not allowed");
@@ -639,7 +655,7 @@ class ConnectionImpl implements Connection {
   public StatementResult execute(Statement statement) {
     Preconditions.checkNotNull(statement);
     ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
-    ParsedStatement parsedStatement = parser.parse(statement);
+    ParsedStatement parsedStatement = parser.parse(statement, this.queryOptions);
     switch (parsedStatement.getType()) {
       case CLIENT_SIDE:
         return parsedStatement
@@ -680,7 +696,7 @@ class ConnectionImpl implements Connection {
     Preconditions.checkNotNull(query);
     Preconditions.checkNotNull(analyzeMode);
     ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG);
-    ParsedStatement parsedStatement = parser.parse(query);
+    ParsedStatement parsedStatement = parser.parse(query, this.queryOptions);
     if (parsedStatement.isQuery()) {
       switch (parsedStatement.getType()) {
         case CLIENT_SIDE:
@@ -809,7 +825,8 @@ class ConnectionImpl implements Connection {
    * Returns the current {@link UnitOfWork} of this connection, or creates a new one based on the
    * current transaction settings of the connection and returns that.
    */
-  private UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork() {
+  @VisibleForTesting
+  UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork() {
     if (this.currentUnitOfWork == null || !this.currentUnitOfWork.isActive()) {
       this.currentUnitOfWork = createNewUnitOfWork();
     }
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionOptions.java b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionOptions.java
index f2e4358..4ec727c 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionOptions.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionOptions.java
@@ -31,6 +31,7 @@ import com.google.cloud.spanner.SpannerOptions;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Sets;
+import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -144,6 +145,7 @@ class ConnectionOptions {
   private static final String DEFAULT_OAUTH_TOKEN = null;
   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 PLAIN_TEXT_PROTOCOL = "http:";
   private static final String HOST_PROTOCOL = "https:";
@@ -166,6 +168,8 @@ class ConnectionOptions {
   public static final String NUM_CHANNELS_PROPERTY_NAME = "numChannels";
   /** Custom user agent string is only for other Google libraries. */
   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";
 
   /** All valid connection properties. */
   public static final Set<ConnectionProperty> VALID_PROPERTIES =
@@ -183,7 +187,8 @@ class ConnectionOptions {
                   ConnectionProperty.createStringProperty(NUM_CHANNELS_PROPERTY_NAME, ""),
                   ConnectionProperty.createBooleanProperty(
                       USE_PLAIN_TEXT_PROPERTY_NAME, "", DEFAULT_USE_PLAIN_TEXT),
-                  ConnectionProperty.createStringProperty(USER_AGENT_PROPERTY_NAME, ""))));
+                  ConnectionProperty.createStringProperty(USER_AGENT_PROPERTY_NAME, ""),
+                  ConnectionProperty.createStringProperty(OPTIMIZER_VERSION_PROPERTY_NAME, ""))));
 
   private static final Set<ConnectionProperty> INTERNAL_PROPERTIES =
       Collections.unmodifiableSet(
@@ -281,6 +286,7 @@ class ConnectionOptions {
      *       false.
      *   <li>retryAbortsInternally (boolean): Sets the initial retryAbortsInternally mode for the
      *       connection. Default is true.
+     *   <li>optimizerVersion (string): Sets the query optimizer version to use for the connection.
      * </ul>
      *
      * @param uri The URI of the Spanner database to connect to.
@@ -373,6 +379,7 @@ class ConnectionOptions {
   private final Credentials credentials;
   private final Integer numChannels;
   private final String userAgent;
+  private final QueryOptions queryOptions;
 
   private final boolean autocommit;
   private final boolean readOnly;
@@ -397,6 +404,9 @@ class ConnectionOptions {
 
     this.usePlainText = parseUsePlainText(this.uri);
     this.userAgent = parseUserAgent(this.uri);
+    QueryOptions.Builder queryOptionsBuilder = QueryOptions.newBuilder();
+    queryOptionsBuilder.setOptimizerVersion(parseOptimizerVersion(this.uri));
+    this.queryOptions = queryOptionsBuilder.build();
 
     this.host =
         matcher.group(Builder.HOST_GROUP) == null
@@ -501,6 +511,12 @@ class ConnectionOptions {
     return value != null ? value : DEFAULT_USER_AGENT;
   }
 
+  @VisibleForTesting
+  static String parseOptimizerVersion(String uri) {
+    String value = parseUriProperty(uri, OPTIMIZER_VERSION_PROPERTY_NAME);
+    return value != null ? value : DEFAULT_OPTIMIZER_VERSION;
+  }
+
   @VisibleForTesting
   static String parseUriProperty(String uri, String property) {
     Pattern pattern = Pattern.compile(String.format("(?is)(?:;|\\?)%s=(.*?)(?:;|$)", property));
@@ -633,6 +649,11 @@ class ConnectionOptions {
     return userAgent;
   }
 
+  /** The {@link QueryOptions} to use for the connection. */
+  QueryOptions getQueryOptions() {
+    return queryOptions;
+  }
+
   /** Interceptors that should be executed after each statement */
   List<StatementExecutionInterceptor> getStatementExecutionInterceptors() {
     return statementExecutionInterceptors;
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutor.java b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutor.java
index 4e79f25..4de1f3f 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutor.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutor.java
@@ -60,6 +60,10 @@ interface ConnectionStatementExecutor {
 
   StatementResult statementShowReadOnlyStaleness();
 
+  StatementResult statementSetOptimizerVersion(String optimizerVersion);
+
+  StatementResult statementShowOptimizerVersion();
+
   StatementResult statementBeginTransaction();
 
   StatementResult statementCommit();
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutorImpl.java b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutorImpl.java
index 7f7a5cf..c8aa40d 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutorImpl.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutorImpl.java
@@ -23,6 +23,7 @@ import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementT
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.RUN_BATCH;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SET_AUTOCOMMIT;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SET_AUTOCOMMIT_DML_MODE;
+import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SET_OPTIMIZER_VERSION;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SET_READONLY;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SET_READ_ONLY_STALENESS;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SET_RETRY_ABORTS_INTERNALLY;
@@ -31,6 +32,7 @@ import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementT
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SHOW_AUTOCOMMIT;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SHOW_AUTOCOMMIT_DML_MODE;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SHOW_COMMIT_TIMESTAMP;
+import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SHOW_OPTIMIZER_VERSION;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SHOW_READONLY;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SHOW_READ_ONLY_STALENESS;
 import static com.google.cloud.spanner.jdbc.StatementResult.ClientSideStatementType.SHOW_READ_TIMESTAMP;
@@ -183,6 +185,18 @@ class ConnectionStatementExecutorImpl implements ConnectionStatementExecutor {
         SHOW_READ_ONLY_STALENESS);
   }
 
+  @Override
+  public StatementResult statementSetOptimizerVersion(String optimizerVersion) {
+    getConnection().setOptimizerVersion(optimizerVersion);
+    return noResult(SET_OPTIMIZER_VERSION);
+  }
+
+  @Override
+  public StatementResult statementShowOptimizerVersion() {
+    return resultSet(
+        "OPTIMIZER_VERSION", getConnection().getOptimizerVersion(), SHOW_OPTIMIZER_VERSION);
+  }
+
   @Override
   public StatementResult statementBeginTransaction() {
     getConnection().beginTransaction();
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java
index c43f979..229c0a6 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java
@@ -86,6 +86,7 @@ import java.util.regex.Pattern;
  *       connection. Default is true. @see {@link
  *       com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection#setRetryAbortsInternally(boolean)}
  *       for more information.
+ *   <li>optimizerVersion (string): The query optimizer version to use for the connection. The value must be either a valid version number or <code>LATEST</code>. If no value is specified, the query optimizer version specified in the environment variable <code>SPANNER_OPTIMIZER_VERSION<code> will be used. If no query optimizer version is specified in the connection URL or in the environment variable, the default query optimizer version of Cloud Spanner will be used.
  * </ul>
  */
 public class JdbcDriver implements Driver {
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/StatementParser.java b/src/main/java/com/google/cloud/spanner/jdbc/StatementParser.java
index e43a097..e3e998a 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/StatementParser.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/StatementParser.java
@@ -20,10 +20,13 @@ import com.google.cloud.spanner.ErrorCode;
 import com.google.cloud.spanner.SpannerExceptionFactory;
 import com.google.cloud.spanner.Statement;
 import com.google.cloud.spanner.jdbc.ClientSideStatementImpl.CompileException;
+import com.google.cloud.spanner.jdbc.StatementParser.ParsedStatement;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
+import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
 import java.util.Collections;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -66,8 +69,10 @@ class StatementParser {
       return new ParsedStatement(StatementType.DDL, statement, sqlWithoutComments);
     }
 
-    private static ParsedStatement query(Statement statement, String sqlWithoutComments) {
-      return new ParsedStatement(StatementType.QUERY, statement, sqlWithoutComments);
+    private static ParsedStatement query(
+        Statement statement, String sqlWithoutComments, QueryOptions defaultQueryOptions) {
+      return new ParsedStatement(
+          StatementType.QUERY, statement, sqlWithoutComments, defaultQueryOptions);
     }
 
     private static ParsedStatement update(Statement statement, String sqlWithoutComments) {
@@ -91,14 +96,40 @@ class StatementParser {
     }
 
     private ParsedStatement(StatementType type, Statement statement, String sqlWithoutComments) {
+      this(type, statement, sqlWithoutComments, null);
+    }
+
+    private ParsedStatement(
+        StatementType type,
+        Statement statement,
+        String sqlWithoutComments,
+        QueryOptions defaultQueryOptions) {
       Preconditions.checkNotNull(type);
       Preconditions.checkNotNull(statement);
       this.type = type;
       this.clientSideStatement = null;
-      this.statement = statement;
+      this.statement = mergeQueryOptions(statement, defaultQueryOptions);
       this.sqlWithoutComments = sqlWithoutComments;
     }
 
+    @Override
+    public int hashCode() {
+      return Objects.hash(
+          this.type, this.clientSideStatement, this.statement, this.sqlWithoutComments);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (!(other instanceof ParsedStatement)) {
+        return false;
+      }
+      ParsedStatement o = (ParsedStatement) other;
+      return Objects.equals(this.type, o.type)
+          && Objects.equals(this.clientSideStatement, o.clientSideStatement)
+          && Objects.equals(this.statement, o.statement)
+          && Objects.equals(this.sqlWithoutComments, o.sqlWithoutComments);
+    }
+
     StatementType getType() {
       return type;
     }
@@ -148,6 +179,26 @@ class StatementParser {
       return statement;
     }
 
+    /**
+     * Merges the {@link QueryOptions} of the {@link Statement} with the current {@link
+     * QueryOptions} of this connection. The {@link QueryOptions} that are already present on the
+     * statement take precedence above the connection {@link QueryOptions}.
+     */
+    Statement mergeQueryOptions(Statement statement, QueryOptions defaultQueryOptions) {
+      if (defaultQueryOptions == null
+          || defaultQueryOptions.equals(QueryOptions.getDefaultInstance())) {
+        return statement;
+      }
+      if (statement.getQueryOptions() == null) {
+        return statement.toBuilder().withQueryOptions(defaultQueryOptions).build();
+      }
+      return statement
+          .toBuilder()
+          .withQueryOptions(
+              defaultQueryOptions.toBuilder().mergeFrom(statement.getQueryOptions()).build())
+          .build();
+    }
+
     String getSqlWithoutComments() {
       return sqlWithoutComments;
     }
@@ -183,12 +234,16 @@ class StatementParser {
    * @return the parsed and categorized statement.
    */
   ParsedStatement parse(Statement statement) {
+    return parse(statement, null);
+  }
+
+  ParsedStatement parse(Statement statement, QueryOptions defaultQueryOptions) {
     String sql = removeCommentsAndTrim(statement.getSql());
     ClientSideStatementImpl client = parseClientSideStatement(sql);
     if (client != null) {
       return ParsedStatement.clientSideStatement(client, statement, sql);
     } else if (isQuery(sql)) {
-      return ParsedStatement.query(statement, sql);
+      return ParsedStatement.query(statement, sql, defaultQueryOptions);
     } else if (isUpdateStatement(sql)) {
       return ParsedStatement.update(statement, sql);
     } else if (isDdlStatement(sql)) {
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/StatementResult.java b/src/main/java/com/google/cloud/spanner/jdbc/StatementResult.java
index 78cde9a..9820716 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/StatementResult.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/StatementResult.java
@@ -60,6 +60,8 @@ interface StatementResult {
     SHOW_COMMIT_TIMESTAMP,
     SHOW_READ_ONLY_STALENESS,
     SET_READ_ONLY_STALENESS,
+    SHOW_OPTIMIZER_VERSION,
+    SET_OPTIMIZER_VERSION,
     BEGIN,
     COMMIT,
     ROLLBACK,
diff --git a/src/main/resources/com/google/cloud/spanner/jdbc/ClientSideStatements.json b/src/main/resources/com/google/cloud/spanner/jdbc/ClientSideStatements.json
index 28c20a5..8d8cca2 100644
--- a/src/main/resources/com/google/cloud/spanner/jdbc/ClientSideStatements.json
+++ b/src/main/resources/com/google/cloud/spanner/jdbc/ClientSideStatements.json
@@ -68,6 +68,14 @@
 	  "method": "statementShowReadOnlyStaleness",
 	  "exampleStatements": ["show variable read_only_staleness"]
 	},
+	{
+	  "name": "SHOW VARIABLE OPTIMIZER_VERSION",
+	  "executorName": "ClientSideStatementNoParamExecutor",
+	  "resultType": "RESULT_SET",
+	  "regex": "(?is)\\A\\s*show\\s+variable\\s+optimizer_version\\s*\\z",
+	  "method": "statementShowOptimizerVersion",
+	  "exampleStatements": ["show variable optimizer_version"]
+	},
 	{
 	  "name": "BEGIN TRANSACTION",
 	  "executorName": "ClientSideStatementNoParamExecutor",
@@ -241,6 +249,20 @@
 	    "allowedValues": "'((STRONG)|(MIN_READ_TIMESTAMP)[\\t ]+((\\d{4})-(\\d{2})-(\\d{2})([Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d{1,9})?)([Zz]|([+-])(\\d{2}):(\\d{2})))|(READ_TIMESTAMP)[\\t ]+((\\d{4})-(\\d{2})-(\\d{2})([Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d{1,9})?)([Zz]|([+-])(\\d{2}):(\\d{2})))|(MAX_STALENESS)[\\t ]+((\\d{1,19})(s|ms|us|ns))|(EXACT_STALENESS)[\\t ]+((\\d{1,19})(s|ms|us|ns)))'",
 	    "converterName": "ClientSideStatementValueConverters$ReadOnlyStalenessConverter"
 	  }
+	},
+	{
+	  "name": "SET OPTIMIZER_VERSION = '<version>'|'LATEST'|''",
+	  "executorName": "ClientSideStatementSetExecutor",
+	  "resultType": "NO_RESULT",
+	  "regex": "(?is)\\A\\s*set\\s+optimizer_version\\s*(?:=)\\s*(.*)\\z",
+	  "method": "statementSetOptimizerVersion",
+	  "exampleStatements": ["set optimizer_version='1'", "set optimizer_version='200'", "set optimizer_version='LATEST'", "set optimizer_version=''"],
+	  "setStatement": {
+	    "propertyName": "OPTIMIZER_VERSION",
+	    "separator": "=",
+	    "allowedValues": "'((\\d{1,20})|(LATEST)|())'",
+	    "converterName": "ClientSideStatementValueConverters$StringValueConverter"
+	  }
 	}
 	]
 }
\ No newline at end of file
diff --git a/synth.py b/synth.py
index 259352e..a5ea146 100644
--- a/synth.py
+++ b/synth.py
@@ -14,12 +14,8 @@
 
 """This script is used to synthesize generated parts of this library."""
 
-import synthtool as s
-import synthtool.gcp as gcp
+import synthtool.languages.java as java
 
-
-common_templates = gcp.CommonTemplates()
-templates = common_templates.java_library()
-s.copy(templates, excludes=[
+java.common_templates(excludes=[
     'README.md'
-])
\ No newline at end of file
+])


Test patch:
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/AbortedTest.java b/src/test/java/com/google/cloud/spanner/jdbc/AbortedTest.java
index 4346bde..d10508b 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/AbortedTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/AbortedTest.java
@@ -20,128 +20,18 @@ import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.google.cloud.spanner.MockSpannerServiceImpl;
 import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
 import com.google.cloud.spanner.ResultSet;
 import com.google.cloud.spanner.Statement;
-import com.google.cloud.spanner.admin.database.v1.MockDatabaseAdminImpl;
-import com.google.cloud.spanner.admin.instance.v1.MockInstanceAdminImpl;
 import com.google.cloud.spanner.jdbc.ITAbstractSpannerTest.AbortInterceptor;
 import com.google.cloud.spanner.jdbc.ITAbstractSpannerTest.ITConnection;
 import com.google.cloud.spanner.jdbc.it.ITTransactionRetryTest.CountTransactionRetryListener;
-import com.google.protobuf.ListValue;
-import com.google.protobuf.Value;
-import com.google.spanner.v1.ResultSetMetadata;
-import com.google.spanner.v1.StructType;
-import com.google.spanner.v1.StructType.Field;
-import com.google.spanner.v1.Type;
-import com.google.spanner.v1.TypeCode;
-import io.grpc.Server;
-import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.Arrays;
-import java.util.List;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 @RunWith(JUnit4.class)
-public class AbortedTest {
-  private static final Statement SELECT_COUNT =
-      Statement.of("SELECT COUNT(*) AS C FROM TEST WHERE ID=1");
-  private static final ResultSetMetadata SELECT_COUNT_METADATA =
-      ResultSetMetadata.newBuilder()
-          .setRowType(
-              StructType.newBuilder()
-                  .addFields(
-                      Field.newBuilder()
-                          .setName("C")
-                          .setType(Type.newBuilder().setCode(TypeCode.INT64).build())
-                          .build())
-                  .build())
-          .build();
-  private static final com.google.spanner.v1.ResultSet SELECT_COUNT_RESULTSET_BEFORE_INSERT =
-      com.google.spanner.v1.ResultSet.newBuilder()
-          .addRows(
-              ListValue.newBuilder()
-                  .addValues(Value.newBuilder().setStringValue("0").build())
-                  .build())
-          .setMetadata(SELECT_COUNT_METADATA)
-          .build();
-  private static final com.google.spanner.v1.ResultSet SELECT_COUNT_RESULTSET_AFTER_INSERT =
-      com.google.spanner.v1.ResultSet.newBuilder()
-          .addRows(
-              ListValue.newBuilder()
-                  .addValues(Value.newBuilder().setStringValue("1").build())
-                  .build())
-          .setMetadata(SELECT_COUNT_METADATA)
-          .build();
-  private static final Statement INSERT_STATEMENT =
-      Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test aborted')");
-  private static final int UPDATE_COUNT = 1;
-
-  private static MockSpannerServiceImpl mockSpanner;
-  private static MockInstanceAdminImpl mockInstanceAdmin;
-  private static MockDatabaseAdminImpl mockDatabaseAdmin;
-  private static Server server;
-  private static InetSocketAddress address;
-
-  @BeforeClass
-  public static void startStaticServer() throws IOException {
-    mockSpanner = new MockSpannerServiceImpl();
-    mockSpanner.setAbortProbability(0.0D); // We don't want any unpredictable aborted transactions.
-    mockInstanceAdmin = new MockInstanceAdminImpl();
-    mockDatabaseAdmin = new MockDatabaseAdminImpl();
-    address = new InetSocketAddress("localhost", 0);
-    server =
-        NettyServerBuilder.forAddress(address)
-            .addService(mockSpanner)
-            .addService(mockInstanceAdmin)
-            .addService(mockDatabaseAdmin)
-            .build()
-            .start();
-  }
-
-  @AfterClass
-  public static void stopServer() throws Exception {
-    SpannerPool.closeSpannerPool();
-    server.shutdown();
-    server.awaitTermination();
-  }
-
-  public ITConnection createConnection(
-      AbortInterceptor interceptor, TransactionRetryListener transactionRetryListener) {
-    return createConnection(
-        Arrays.<StatementExecutionInterceptor>asList(interceptor),
-        Arrays.<TransactionRetryListener>asList(transactionRetryListener));
-  }
-
-  public ITConnection createConnection(
-      List<StatementExecutionInterceptor> interceptors,
-      List<TransactionRetryListener> transactionRetryListeners) {
-    StringBuilder url =
-        new StringBuilder(
-            String.format(
-                "cloudspanner://localhost:%d/projects/proj/instances/inst/databases/db?usePlainText=true;autocommit=false;retryAbortsInternally=true",
-                server.getPort()));
-    ConnectionOptions.Builder builder =
-        ConnectionOptions.newBuilder()
-            .setUri(url.toString())
-            .setStatementExecutionInterceptors(interceptors);
-    ConnectionOptions options = builder.build();
-    ITConnection connection = createITConnection(options);
-    for (TransactionRetryListener listener : transactionRetryListeners) {
-      connection.addTransactionRetryListener(listener);
-    }
-    return connection;
-  }
-
-  private ITConnection createITConnection(ConnectionOptions options) {
-    return new ITConnectionImpl(options);
-  }
+public class AbortedTest extends AbstractMockServerTest {
 
   @Test
   public void testCommitAborted() {
@@ -149,7 +39,7 @@ public class AbortedTest {
     // transaction is the most recent transaction of that session.
     for (int i = 0; i < 2; i++) {
       mockSpanner.putStatementResult(
-          StatementResult.query(SELECT_COUNT, SELECT_COUNT_RESULTSET_BEFORE_INSERT));
+          StatementResult.query(SELECT_COUNT_STATEMENT, SELECT_COUNT_RESULTSET_BEFORE_INSERT));
       mockSpanner.putStatementResult(StatementResult.update(INSERT_STATEMENT, UPDATE_COUNT));
       AbortInterceptor interceptor = new AbortInterceptor(0);
       try (ITConnection connection =
@@ -170,7 +60,7 @@ public class AbortedTest {
         // do a commit that will first abort, and then on retry will succeed
         connection.commit();
         mockSpanner.putStatementResult(
-            StatementResult.query(SELECT_COUNT, SELECT_COUNT_RESULTSET_AFTER_INSERT));
+            StatementResult.query(SELECT_COUNT_STATEMENT, SELECT_COUNT_RESULTSET_AFTER_INSERT));
         // verify that the insert succeeded
         try (ResultSet rs =
             connection.executeQuery(Statement.of("SELECT COUNT(*) AS C FROM TEST WHERE ID=1"))) {
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/AbstractConnectionImplTest.java b/src/test/java/com/google/cloud/spanner/jdbc/AbstractConnectionImplTest.java
index 8b4bdf7..f12f5e8 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/AbstractConnectionImplTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/AbstractConnectionImplTest.java
@@ -608,6 +608,51 @@ public abstract class AbstractConnectionImplTest {
     }
   }
 
+  boolean isSetOptimizerVersionAllowed() {
+    return !getConnection().isClosed();
+  }
+
+  @Test
+  public void testSetOptimizerVersion() {
+    try (Connection connection = getConnection()) {
+      if (isSetOptimizerVersionAllowed()) {
+        for (String version : new String[] {"1", "2", "latest", ""}) {
+          log("SET OPTIMIZER_VERSION='" + version + "';");
+          connection.setOptimizerVersion(version);
+
+          log("@EXPECT RESULT_SET 'OPTIMIZER_VERSION','" + version + "'");
+          log("SHOW VARIABLE OPTIMIZER_VERSION;");
+          assertThat(connection.getOptimizerVersion(), is(equalTo(version)));
+        }
+      } else {
+        log("@EXPECT EXCEPTION FAILED_PRECONDITION");
+        log("SET OPTIMIZER_VERSION='1';");
+        exception.expect(matchCode(ErrorCode.FAILED_PRECONDITION));
+        connection.setOptimizerVersion("1");
+      }
+    }
+  }
+
+  boolean isGetOptimizerVersionAllowed() {
+    return !getConnection().isClosed();
+  }
+
+  @Test
+  public void testGetOptimizerVersion() {
+    try (Connection connection = getConnection()) {
+      if (isGetOptimizerVersionAllowed()) {
+        log("@EXPECT RESULT_SET 'OPTIMIZER_VERSION'");
+        log("SHOW VARIABLE OPTIMIZER_VERSION;");
+        assertThat(connection.getOptimizerVersion(), is(notNullValue()));
+      } else {
+        log("@EXPECT EXCEPTION FAILED_PRECONDITION");
+        log("SHOW VARIABLE OPTIMIZER_VERSION;");
+        exception.expect(matchCode(ErrorCode.FAILED_PRECONDITION));
+        connection.getOptimizerVersion();
+      }
+    }
+  }
+
   abstract boolean isCommitAllowed();
 
   @Test
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/AbstractMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/AbstractMockServerTest.java
new file mode 100644
index 0000000..4117eed
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/AbstractMockServerTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.jdbc;
+
+import com.google.cloud.spanner.MockSpannerServiceImpl;
+import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
+import com.google.cloud.spanner.Statement;
+import com.google.cloud.spanner.admin.database.v1.MockDatabaseAdminImpl;
+import com.google.cloud.spanner.admin.instance.v1.MockInstanceAdminImpl;
+import com.google.cloud.spanner.jdbc.ITAbstractSpannerTest.AbortInterceptor;
+import com.google.cloud.spanner.jdbc.ITAbstractSpannerTest.ITConnection;
+import com.google.protobuf.AbstractMessage;
+import com.google.protobuf.ListValue;
+import com.google.protobuf.Value;
+import com.google.spanner.v1.ExecuteSqlRequest;
+import com.google.spanner.v1.ResultSetMetadata;
+import com.google.spanner.v1.StructType;
+import com.google.spanner.v1.StructType.Field;
+import com.google.spanner.v1.Type;
+import com.google.spanner.v1.TypeCode;
+import io.grpc.Server;
+import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public abstract class AbstractMockServerTest {
+  static final long COUNT_BEFORE_INSERT = 0L;
+  static final long COUNT_AFTER_INSERT = 1L;
+  static final Statement SELECT_COUNT_STATEMENT =
+      Statement.of("SELECT COUNT(*) AS C FROM TEST WHERE ID=1");
+  private static final ResultSetMetadata SELECT_COUNT_METADATA =
+      ResultSetMetadata.newBuilder()
+          .setRowType(
+              StructType.newBuilder()
+                  .addFields(
+                      Field.newBuilder()
+                          .setName("C")
+                          .setType(Type.newBuilder().setCode(TypeCode.INT64).build())
+                          .build())
+                  .build())
+          .build();
+  static final com.google.spanner.v1.ResultSet SELECT_COUNT_RESULTSET_BEFORE_INSERT =
+      com.google.spanner.v1.ResultSet.newBuilder()
+          .addRows(
+              ListValue.newBuilder()
+                  .addValues(
+                      Value.newBuilder()
+                          .setStringValue(String.valueOf(COUNT_BEFORE_INSERT))
+                          .build())
+                  .build())
+          .setMetadata(SELECT_COUNT_METADATA)
+          .build();
+  static final com.google.spanner.v1.ResultSet SELECT_COUNT_RESULTSET_AFTER_INSERT =
+      com.google.spanner.v1.ResultSet.newBuilder()
+          .addRows(
+              ListValue.newBuilder()
+                  .addValues(
+                      Value.newBuilder().setStringValue(String.valueOf(COUNT_AFTER_INSERT)).build())
+                  .build())
+          .setMetadata(SELECT_COUNT_METADATA)
+          .build();
+  static final Statement INSERT_STATEMENT =
+      Statement.of("INSERT INTO TEST (ID, NAME) VALUES (1, 'test aborted')");
+  static final int UPDATE_COUNT = 1;
+
+  static MockSpannerServiceImpl mockSpanner;
+  static MockInstanceAdminImpl mockInstanceAdmin;
+  static MockDatabaseAdminImpl mockDatabaseAdmin;
+  private static Server server;
+  private static InetSocketAddress address;
+
+  @BeforeClass
+  public static void startStaticServer() throws IOException {
+    mockSpanner = new MockSpannerServiceImpl();
+    mockSpanner.setAbortProbability(0.0D); // We don't want any unpredictable aborted transactions.
+    mockInstanceAdmin = new MockInstanceAdminImpl();
+    mockDatabaseAdmin = new MockDatabaseAdminImpl();
+    address = new InetSocketAddress("localhost", 0);
+    server =
+        NettyServerBuilder.forAddress(address)
+            .addService(mockSpanner)
+            .addService(mockInstanceAdmin)
+            .addService(mockDatabaseAdmin)
+            .build()
+            .start();
+    mockSpanner.putStatementResult(
+        StatementResult.query(SELECT_COUNT_STATEMENT, SELECT_COUNT_RESULTSET_BEFORE_INSERT));
+    mockSpanner.putStatementResult(StatementResult.update(INSERT_STATEMENT, UPDATE_COUNT));
+  }
+
+  @AfterClass
+  public static void stopServer() throws Exception {
+    SpannerPool.closeSpannerPool();
+    server.shutdown();
+    server.awaitTermination();
+  }
+
+  @Before
+  public void setupResults() {
+    mockSpanner.reset();
+  }
+
+  @After
+  public void closeSpannerPool() {
+    SpannerPool.closeSpannerPool();
+  }
+
+  java.sql.Connection createJdbcConnection() throws SQLException {
+    return DriverManager.getConnection("jdbc:" + getBaseUrl());
+  }
+
+  ITConnection createConnection() {
+    return createConnection(
+        Collections.<StatementExecutionInterceptor>emptyList(),
+        Collections.<TransactionRetryListener>emptyList());
+  }
+
+  ITConnection createConnection(
+      AbortInterceptor interceptor, TransactionRetryListener transactionRetryListener) {
+    return createConnection(
+        Arrays.<StatementExecutionInterceptor>asList(interceptor),
+        Arrays.<TransactionRetryListener>asList(transactionRetryListener));
+  }
+
+  ITConnection createConnection(
+      List<StatementExecutionInterceptor> interceptors,
+      List<TransactionRetryListener> transactionRetryListeners) {
+    StringBuilder url = new StringBuilder(getBaseUrl());
+    ConnectionOptions.Builder builder =
+        ConnectionOptions.newBuilder()
+            .setUri(url.toString())
+            .setStatementExecutionInterceptors(interceptors);
+    ConnectionOptions options = builder.build();
+    ITConnection connection = createITConnection(options);
+    for (TransactionRetryListener listener : transactionRetryListeners) {
+      connection.addTransactionRetryListener(listener);
+    }
+    return connection;
+  }
+
+  String getBaseUrl() {
+    return String.format(
+        "cloudspanner://localhost:%d/projects/proj/instances/inst/databases/db?usePlainText=true;autocommit=false;retryAbortsInternally=true",
+        server.getPort());
+  }
+
+  ExecuteSqlRequest getLastExecuteSqlRequest() {
+    List<AbstractMessage> requests = mockSpanner.getRequests();
+    for (int i = requests.size() - 1; i >= 0; i--) {
+      if (requests.get(i) instanceof ExecuteSqlRequest) {
+        return (ExecuteSqlRequest) requests.get(i);
+      }
+    }
+    throw new IllegalStateException("No ExecuteSqlRequest found in requests");
+  }
+
+  private ITConnection createITConnection(ConnectionOptions options) {
+    return new ITConnectionImpl(options);
+  }
+}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionImplTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionImplTest.java
index 38c9787..2c2cb96 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionImplTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionImplTest.java
@@ -24,10 +24,12 @@ import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyListOf;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.api.core.ApiFuture;
@@ -38,6 +40,8 @@ import com.google.cloud.Timestamp;
 import com.google.cloud.spanner.DatabaseClient;
 import com.google.cloud.spanner.ErrorCode;
 import com.google.cloud.spanner.ForwardingResultSet;
+import com.google.cloud.spanner.Options;
+import com.google.cloud.spanner.Options.QueryOption;
 import com.google.cloud.spanner.ReadContext.QueryAnalyzeMode;
 import com.google.cloud.spanner.ReadOnlyTransaction;
 import com.google.cloud.spanner.ResultSet;
@@ -57,6 +61,7 @@ import com.google.cloud.spanner.jdbc.ConnectionStatementExecutorImpl.StatementTi
 import com.google.cloud.spanner.jdbc.ReadOnlyStalenessUtil.GetExactStaleness;
 import com.google.cloud.spanner.jdbc.StatementResult.ResultType;
 import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
+import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
 import com.google.spanner.v1.ResultSetStats;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -523,6 +528,76 @@ public class ConnectionImplTest {
     }
   }
 
+  @Test
+  public void testExecuteSetOptimizerVersion() {
+    try (ConnectionImpl subject =
+        createConnection(
+            ConnectionOptions.newBuilder()
+                .setCredentials(NoCredentials.getInstance())
+                .setUri(URI)
+                .build())) {
+      assertThat(subject.getOptimizerVersion(), is(equalTo("")));
+
+      StatementResult res = subject.execute(Statement.of("set optimizer_version='1'"));
+      assertThat(res.getResultType(), is(equalTo(ResultType.NO_RESULT)));
+      assertThat(subject.getOptimizerVersion(), is(equalTo("1")));
+
+      res = subject.execute(Statement.of("set optimizer_version='1000'"));
+      assertThat(res.getResultType(), is(equalTo(ResultType.NO_RESULT)));
+      assertThat(subject.getOptimizerVersion(), is(equalTo("1000")));
+
+      res = subject.execute(Statement.of("set optimizer_version='latest'"));
+      assertThat(res.getResultType(), is(equalTo(ResultType.NO_RESULT)));
+      assertThat(subject.getOptimizerVersion(), is(equalTo("latest")));
+
+      res = subject.execute(Statement.of("set optimizer_version=''"));
+      assertThat(res.getResultType(), is(equalTo(ResultType.NO_RESULT)));
+      assertThat(subject.getOptimizerVersion(), is(equalTo("")));
+    }
+  }
+
+  @Test
+  public void testExecuteSetOptimizerVersionInvalidValue() {
+    try (ConnectionImpl subject =
+        createConnection(
+            ConnectionOptions.newBuilder()
+                .setCredentials(NoCredentials.getInstance())
+                .setUri(URI)
+                .build())) {
+      assertThat(subject.getOptimizerVersion(), is(equalTo("")));
+
+      try {
+        subject.execute(Statement.of("set optimizer_version='NOT_A_VERSION'"));
+        fail("Missing expected exception");
+      } catch (SpannerException e) {
+        assertThat(e.getErrorCode(), is(equalTo(ErrorCode.INVALID_ARGUMENT)));
+      }
+    }
+  }
+
+  @Test
+  public void testExecuteGetOptimizerVersion() {
+    try (ConnectionImpl subject =
+        createConnection(
+            ConnectionOptions.newBuilder()
+                .setCredentials(NoCredentials.getInstance())
+                .setUri(URI)
+                .build())) {
+      assertThat(subject.getOptimizerVersion(), is(equalTo("")));
+
+      StatementResult res = subject.execute(Statement.of("show variable optimizer_version"));
+      assertThat(res.getResultType(), is(equalTo(ResultType.RESULT_SET)));
+      assertThat(res.getResultSet().next(), is(true));
+      assertThat(res.getResultSet().getString("OPTIMIZER_VERSION"), is(equalTo("")));
+
+      subject.execute(Statement.of("set optimizer_version='1'"));
+      res = subject.execute(Statement.of("show variable optimizer_version"));
+      assertThat(res.getResultType(), is(equalTo(ResultType.RESULT_SET)));
+      assertThat(res.getResultSet().next(), is(true));
+      assertThat(res.getResultSet().getString("OPTIMIZER_VERSION"), is(equalTo("1")));
+    }
+  }
+
   @Test
   public void testExecuteSetStatementTimeout() {
     try (ConnectionImpl subject =
@@ -1114,4 +1189,73 @@ public class ConnectionImplTest {
       assertThat(subject.removeTransactionRetryListener(listener), is(false));
     }
   }
+
+  @Test
+  public void testMergeQueryOptions() {
+    ConnectionOptions connectionOptions = mock(ConnectionOptions.class);
+    SpannerPool spannerPool = mock(SpannerPool.class);
+    DdlClient ddlClient = mock(DdlClient.class);
+    DatabaseClient dbClient = mock(DatabaseClient.class);
+    final UnitOfWork unitOfWork = mock(UnitOfWork.class);
+    try (ConnectionImpl impl =
+        new ConnectionImpl(connectionOptions, spannerPool, ddlClient, dbClient) {
+          @Override
+          UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork() {
+            return unitOfWork;
+          }
+        }) {
+      // Execute query with an optimizer version set on the connection.
+      impl.setOptimizerVersion("1");
+      impl.executeQuery(Statement.of("SELECT FOO FROM BAR"));
+      verify(unitOfWork)
+          .executeQuery(
+              StatementParser.INSTANCE.parse(
+                  Statement.newBuilder("SELECT FOO FROM BAR")
+                      .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("1").build())
+                      .build()),
+              AnalyzeMode.NONE);
+
+      // Execute query with an optimizer version set on the connection.
+      impl.setOptimizerVersion("2");
+      impl.executeQuery(Statement.of("SELECT FOO FROM BAR"));
+      verify(unitOfWork)
+          .executeQuery(
+              StatementParser.INSTANCE.parse(
+                  Statement.newBuilder("SELECT FOO FROM BAR")
+                      .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("2").build())
+                      .build()),
+              AnalyzeMode.NONE);
+
+      // Execute query with an optimizer version set on the connection and PrefetchChunks query
+      // option specified for the query.
+      QueryOption prefetchOption = Options.prefetchChunks(100);
+      impl.setOptimizerVersion("3");
+      impl.executeQuery(Statement.of("SELECT FOO FROM BAR"), prefetchOption);
+      verify(unitOfWork)
+          .executeQuery(
+              StatementParser.INSTANCE.parse(
+                  Statement.newBuilder("SELECT FOO FROM BAR")
+                      .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("3").build())
+                      .build()),
+              AnalyzeMode.NONE,
+              prefetchOption);
+
+      // Execute query with an optimizer version 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.executeQuery(
+          Statement.newBuilder("SELECT FOO FROM BAR")
+              .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("5").build())
+              .build(),
+          prefetchOption);
+      verify(unitOfWork)
+          .executeQuery(
+              StatementParser.INSTANCE.parse(
+                  Statement.newBuilder("SELECT FOO FROM BAR")
+                      .withQueryOptions(QueryOptions.newBuilder().setOptimizerVersion("5").build())
+                      .build()),
+              AnalyzeMode.NONE,
+              prefetchOption);
+    }
+  }
 }
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutorTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutorTest.java
index debd02b..6e524c4 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutorTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementExecutorTest.java
@@ -95,6 +95,12 @@ public class ConnectionStatementExecutorTest {
     verify(connection).getReadOnlyStaleness();
   }
 
+  @Test
+  public void testStatementGetOptimizerVersion() {
+    subject.statementShowOptimizerVersion();
+    verify(connection).getOptimizerVersion();
+  }
+
   @Test
   public void testStatementGetReadTimestamp() {
     subject.statementShowReadTimestamp();
@@ -167,6 +173,16 @@ public class ConnectionStatementExecutorTest {
         .setReadOnlyStaleness(TimestampBound.ofMaxStaleness(20L, TimeUnit.MILLISECONDS));
   }
 
+  @Test
+  public void testStatementSetOptimizerVersion() {
+    subject.statementSetOptimizerVersion("1");
+    verify(connection).setOptimizerVersion("1");
+    subject.statementSetOptimizerVersion("");
+    verify(connection).setOptimizerVersion("");
+    subject.statementSetOptimizerVersion("LATEST");
+    verify(connection).setOptimizerVersion("LATEST");
+  }
+
   @Test
   public void testStatementSetStatementTimeout() {
     subject.statementSetStatementTimeout(Duration.newBuilder().setNanos(100).build());
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementWithNoParametersTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementWithNoParametersTest.java
index e106bcd..51aa8ab 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementWithNoParametersTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementWithNoParametersTest.java
@@ -114,6 +114,18 @@ public class ConnectionStatementWithNoParametersTest {
     verify(connection, times(1)).getReadOnlyStaleness();
   }
 
+  @Test
+  public void testExecuteGetOptimizerVersion() throws Exception {
+    ParsedStatement statement = parser.parse(Statement.of("show variable optimizer_version"));
+    ConnectionImpl connection = mock(ConnectionImpl.class);
+    ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class);
+    when(executor.getConnection()).thenReturn(connection);
+    when(executor.statementShowOptimizerVersion()).thenCallRealMethod();
+    when(connection.getOptimizerVersion()).thenReturn("1");
+    statement.getClientSideStatement().execute(executor, "show variable optimizer_version");
+    verify(connection, times(1)).getOptimizerVersion();
+  }
+
   @Test
   public void testExecuteBegin() throws Exception {
     ParsedStatement subject = parser.parse(Statement.of("begin"));
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementWithOneParameterTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementWithOneParameterTest.java
index de5354f..b9ebf1e 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementWithOneParameterTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionStatementWithOneParameterTest.java
@@ -148,6 +148,21 @@ public class ConnectionStatementWithOneParameterTest {
     }
   }
 
+  @Test
+  public void testExecuteSetOptimizerVersion() throws Exception {
+    ParsedStatement subject = parser.parse(Statement.of("set optimizer_version='foo'"));
+    ConnectionImpl connection = mock(ConnectionImpl.class);
+    ConnectionStatementExecutorImpl executor = mock(ConnectionStatementExecutorImpl.class);
+    when(executor.getConnection()).thenReturn(connection);
+    when(executor.statementSetOptimizerVersion(any(String.class))).thenCallRealMethod();
+    for (String version : new String[] {"1", "200", "", "LATEST"}) {
+      subject
+          .getClientSideStatement()
+          .execute(executor, String.format("set optimizer_version='%s'", version));
+      verify(connection, times(1)).setOptimizerVersion(version);
+    }
+  }
+
   @Test
   public void testExecuteSetTransaction() throws Exception {
     ParsedStatement subject = parser.parse(Statement.of("set transaction read_only"));
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionTest.java
new file mode 100644
index 0000000..d99b9a7
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.jdbc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.cloud.spanner.ResultSet;
+import com.google.cloud.spanner.SpannerOptions;
+import com.google.cloud.spanner.Statement;
+import com.google.spanner.v1.ExecuteSqlRequest;
+import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
+import java.sql.SQLException;
+import org.junit.Test;
+
+public class ConnectionTest extends AbstractMockServerTest {
+  @Test
+  public void testDefaultOptimizerVersion() throws SQLException {
+    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() throws SQLException {
+    try {
+      SpannerOptions.useEnvironment(
+          new SpannerOptions.SpannerEnvironment() {
+            @Override
+            public String getOptimizerVersion() {
+              return "20";
+            }
+          });
+      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)) {
+          assertThat(rs.next()).isTrue();
+          assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT);
+          assertThat(rs.next()).isFalse();
+          // Verify query options from the environment.
+          ExecuteSqlRequest request = getLastExecuteSqlRequest();
+          assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("20");
+        }
+        // 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'"));
+        try (ResultSet rs = connection.executeQuery(SELECT_COUNT_STATEMENT)) {
+          assertThat(rs.next()).isTrue();
+          assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT);
+          assertThat(rs.next()).isFalse();
+
+          ExecuteSqlRequest request = getLastExecuteSqlRequest();
+          // Optimizer version should come from the connection.
+          assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("30");
+        }
+        // Now specify options directly for the query. These should override both the environment
+        // and what is set on the connection.
+        try (ResultSet rs =
+            connection.executeQuery(
+                Statement.newBuilder(SELECT_COUNT_STATEMENT.getSql())
+                    .withQueryOptions(
+                        QueryOptions.newBuilder()
+                            .setOptimizerVersion("user-defined-version")
+                            .build())
+                    .build())) {
+          assertThat(rs.next()).isTrue();
+          assertThat(rs.getLong(0)).isEqualTo(COUNT_BEFORE_INSERT);
+          assertThat(rs.next()).isFalse();
+
+          ExecuteSqlRequest request = getLastExecuteSqlRequest();
+          // Optimizer version should come from the query.
+          assertThat(request.getQueryOptions().getOptimizerVersion())
+              .isEqualTo("user-defined-version");
+        }
+      }
+    } finally {
+      SpannerOptions.useDefaultEnvironment();
+    }
+  }
+}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcQueryOptionsTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcQueryOptionsTest.java
new file mode 100644
index 0000000..460e07f
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcQueryOptionsTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.jdbc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
+import com.google.cloud.spanner.SpannerOptions;
+import com.google.cloud.spanner.Statement;
+import com.google.common.base.MoreObjects;
+import com.google.spanner.v1.ExecuteSqlRequest;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class JdbcQueryOptionsTest extends AbstractMockServerTest {
+  @Test
+  public void testDefaultOptimizerVersion() throws SQLException {
+    try (java.sql.Connection connection = createJdbcConnection()) {
+      try (java.sql.ResultSet rs =
+          connection.createStatement().executeQuery("SHOW VARIABLE OPTIMIZER_VERSION")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo("");
+        assertThat(rs.next()).isFalse();
+      }
+    }
+  }
+
+  @Test
+  public void testOptimizerVersionInConnectionUrl() throws SQLException {
+    try (java.sql.Connection connection =
+        DriverManager.getConnection(
+            String.format("jdbc:%s;optimizerVersion=%s", getBaseUrl(), "100"))) {
+      try (java.sql.ResultSet rs =
+          connection.createStatement().executeQuery("SHOW VARIABLE OPTIMIZER_VERSION")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo("100");
+        assertThat(rs.next()).isFalse();
+      }
+    }
+  }
+
+  @Test
+  public void testSetOptimizerVersion() throws SQLException {
+    try (java.sql.Connection connection = createJdbcConnection()) {
+      connection.createStatement().execute("SET OPTIMIZER_VERSION='20'");
+      try (java.sql.ResultSet rs =
+          connection.createStatement().executeQuery("SHOW VARIABLE OPTIMIZER_VERSION")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo("20");
+        assertThat(rs.next()).isFalse();
+      }
+      connection.createStatement().execute("SET OPTIMIZER_VERSION='latest'");
+      try (java.sql.ResultSet rs =
+          connection.createStatement().executeQuery("SHOW VARIABLE OPTIMIZER_VERSION")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo("latest");
+        assertThat(rs.next()).isFalse();
+      }
+      connection.createStatement().execute("SET OPTIMIZER_VERSION=''");
+      try (java.sql.ResultSet rs =
+          connection.createStatement().executeQuery("SHOW VARIABLE OPTIMIZER_VERSION")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo("");
+        assertThat(rs.next()).isFalse();
+      }
+    }
+  }
+
+  @Test
+  public void testSetAndUseOptimizerVersion() throws SQLException {
+    try (java.sql.Connection connection = createJdbcConnection()) {
+      connection.createStatement().execute("SET OPTIMIZER_VERSION='20'");
+      try (java.sql.ResultSet rs =
+          connection.createStatement().executeQuery(SELECT_COUNT_STATEMENT.getSql())) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(COUNT_BEFORE_INSERT);
+        assertThat(rs.next()).isFalse();
+        // Verify that the last ExecuteSqlRequest that the server received specified optimizer
+        // version 20.
+        ExecuteSqlRequest request = getLastExecuteSqlRequest();
+        assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("20");
+      }
+
+      // Do another query, but now with optimizer version 'latest'.
+      connection.createStatement().execute("SET OPTIMIZER_VERSION='latest'");
+      try (java.sql.ResultSet rs =
+          connection.createStatement().executeQuery(SELECT_COUNT_STATEMENT.getSql())) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(COUNT_BEFORE_INSERT);
+        assertThat(rs.next()).isFalse();
+        // Verify that the last ExecuteSqlRequest that the server received specified optimizer
+        // version 'latest'.
+        ExecuteSqlRequest request = getLastExecuteSqlRequest();
+        assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("latest");
+      }
+
+      // Set the optimizer version to ''. This will do a fallback to the default, meaning that it
+      // will be read from the environment variable SPANNER_OPTIMIZER_VERSION as we have nothing set
+      // on the connection URL.
+      connection.createStatement().execute("SET OPTIMIZER_VERSION=''");
+      try (java.sql.ResultSet rs =
+          connection.createStatement().executeQuery(SELECT_COUNT_STATEMENT.getSql())) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(COUNT_BEFORE_INSERT);
+        assertThat(rs.next()).isFalse();
+        // Verify that the last ExecuteSqlRequest that the server received specified an optimizer
+        // version equal to the environment default.
+        ExecuteSqlRequest request = getLastExecuteSqlRequest();
+        assertThat(request.getQueryOptions().getOptimizerVersion())
+            .isEqualTo(MoreObjects.firstNonNull(System.getenv("SPANNER_OPTIMIZER_VERSION"), ""));
+      }
+    }
+  }
+
+  @Test
+  public void testUseOptimizerVersionFromConnectionUrl() throws SQLException {
+    try (java.sql.Connection connection =
+        DriverManager.getConnection(String.format("jdbc:%s;optimizerVersion=10", getBaseUrl()))) {
+      // Do a query and verify that the version from the connection URL is used.
+      try (java.sql.ResultSet rs =
+          connection.createStatement().executeQuery(SELECT_COUNT_STATEMENT.getSql())) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(COUNT_BEFORE_INSERT);
+        assertThat(rs.next()).isFalse();
+        // The optimizer version used should be '10' from the connection URL.
+        ExecuteSqlRequest request = getLastExecuteSqlRequest();
+        assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("10");
+      }
+    }
+  }
+
+  @Test
+  public void testUseOptimizerVersionFromEnvironment() throws SQLException {
+    try {
+      SpannerOptions.useEnvironment(
+          new SpannerOptions.SpannerEnvironment() {
+            @Override
+            public String getOptimizerVersion() {
+              return "20";
+            }
+          });
+      try (java.sql.Connection connection =
+          DriverManager.getConnection(String.format("jdbc:%s", getBaseUrl()))) {
+        // Do a query and verify that the version from the environment is used.
+        try (java.sql.ResultSet rs =
+            connection.createStatement().executeQuery(SELECT_COUNT_STATEMENT.getSql())) {
+          assertThat(rs.next()).isTrue();
+          assertThat(rs.getLong(1)).isEqualTo(COUNT_BEFORE_INSERT);
+          assertThat(rs.next()).isFalse();
+          // Verify query options from the environment.
+          ExecuteSqlRequest request = getLastExecuteSqlRequest();
+          assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("20");
+        }
+        // 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.createStatement().execute("SET OPTIMIZER_VERSION='30'");
+        try (java.sql.ResultSet rs =
+            connection.createStatement().executeQuery(SELECT_COUNT_STATEMENT.getSql())) {
+          assertThat(rs.next()).isTrue();
+          assertThat(rs.getLong(1)).isEqualTo(COUNT_BEFORE_INSERT);
+          assertThat(rs.next()).isFalse();
+
+          ExecuteSqlRequest request = getLastExecuteSqlRequest();
+          // Optimizer version should come from the connection.
+          assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("30");
+        }
+      }
+    } finally {
+      SpannerOptions.useDefaultEnvironment();
+    }
+  }
+
+  @Test
+  public void testUseQueryHint() throws SQLException {
+    mockSpanner.putStatementResult(
+        StatementResult.query(
+            Statement.of(
+                String.format("@{optimizer_version=1} %s", SELECT_COUNT_STATEMENT.getSql())),
+            SELECT_COUNT_RESULTSET_BEFORE_INSERT));
+    try (java.sql.Connection connection =
+        DriverManager.getConnection(String.format("jdbc:%s", getBaseUrl()))) {
+      try (java.sql.ResultSet rs =
+          connection
+              .createStatement()
+              .executeQuery(
+                  String.format("@{optimizer_version=1} %s", SELECT_COUNT_STATEMENT.getSql()))) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(COUNT_BEFORE_INSERT);
+        assertThat(rs.next()).isFalse();
+        // The optimizer version used in the ExecuteSqlRequest should be empty as the query hint is
+        // parsed by the backend.
+        ExecuteSqlRequest request = getLastExecuteSqlRequest();
+        assertThat(request.getQueryOptions().getOptimizerVersion()).isEqualTo("");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcTypeConverterTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcTypeConverterTest.java
index 773fcf9..328c01f 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcTypeConverterTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcTypeConverterTest.java
@@ -50,6 +50,7 @@ import java.util.Arrays;
 import java.util.Calendar;
 import java.util.List;
 import java.util.TimeZone;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -580,6 +581,7 @@ public class JdbcTypeConverterTest {
     }
   }
 
+  @Ignore("ignore until java-core 1.93.3 is available")
   @SuppressWarnings("deprecation")
   @Test
   public void testToGoogleTimestamp() {
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcQueryOptionsTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcQueryOptionsTest.java
new file mode 100644
index 0000000..8e72aa2
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcQueryOptionsTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.jdbc.it;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.cloud.spanner.IntegrationTest;
+import com.google.cloud.spanner.SpannerOptions;
+import com.google.cloud.spanner.jdbc.ITAbstractJdbcTest;
+import com.google.cloud.spanner.jdbc.JdbcSqlException;
+import com.google.cloud.spanner.jdbc.SpannerPool;
+import com.google.rpc.Code;
+import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * {@link QueryOptions} can be used with the JDBC driver on three different levels:
+ *
+ * <ol>
+ *   <li>Specify {@link QueryOptions} in environment variables.
+ *   <li>Specify {@link QueryOptions} in the connection URL or by setting these using <code>
+ *       SET &lt;query_option&gt; = '&lt;value&gt;'</code> statements.
+ *   <li>Specify {@link QueryOptions} in a query hint.
+ * </ol>
+ *
+ * This class tests all three possibilities.
+ */
+@Category(IntegrationTest.class)
+@RunWith(JUnit4.class)
+public class ITJdbcQueryOptionsTest extends ITAbstractJdbcTest {
+  private String connectionUriSuffix;
+
+  @Before
+  public void resetConnectionUriSuffix() {
+    connectionUriSuffix = "";
+  }
+
+  @After
+  public void closeSpannerPool() {
+    // Close the pool after each test to prevent side effects from one test case to affect following
+    // test cases.
+    SpannerPool.closeSpannerPool();
+  }
+
+  @Override
+  protected void appendConnectionUri(StringBuilder uri) {
+    uri.append(connectionUriSuffix);
+  }
+
+  private void verifyOptimizerVersion(Connection connection, String expectedVersion)
+      throws SQLException {
+    try (ResultSet rs =
+        connection.createStatement().executeQuery("SHOW VARIABLE OPTIMIZER_VERSION")) {
+      assertThat(rs.next()).isTrue();
+      assertThat(rs.getString("OPTIMIZER_VERSION")).isEqualTo(expectedVersion);
+      assertThat(rs.next()).isFalse();
+    }
+  }
+
+  @Test
+  public void connectionUrl() throws SQLException {
+    this.connectionUriSuffix = ";optimizerVersion=1";
+    try (Connection connection = createConnection()) {
+      verifyOptimizerVersion(connection, "1");
+      try (ResultSet rs = connection.createStatement().executeQuery("SELECT 1")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(1L);
+        assertThat(rs.next()).isFalse();
+      }
+    }
+  }
+
+  @Test
+  public void connectionUrlWithInvalidOptimizerVersion() throws SQLException {
+    this.connectionUriSuffix = ";optimizerVersion=9999999";
+    try (Connection connection = createConnection()) {
+      try (ResultSet rs = connection.createStatement().executeQuery("SELECT 1")) {
+        fail("missing expected exception");
+      } catch (SQLException e) {
+        assertThat((Throwable) e).isInstanceOf(JdbcSqlException.class);
+        JdbcSqlException je = (JdbcSqlException) e;
+        assertThat(je.getCode()).isEqualTo(Code.INVALID_ARGUMENT);
+      }
+    }
+  }
+
+  @Test
+  public void setOptimizerVersion() throws SQLException {
+    try (Connection connection = createConnection()) {
+      verifyOptimizerVersion(connection, "");
+      connection.createStatement().execute("SET OPTIMIZER_VERSION='1'");
+      verifyOptimizerVersion(connection, "1");
+      try (ResultSet rs = connection.createStatement().executeQuery("SELECT 1")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(1L);
+        assertThat(rs.next()).isFalse();
+      }
+    }
+  }
+
+  @Test
+  public void setLatestOptimizerVersion() throws SQLException {
+    try (Connection connection = createConnection()) {
+      verifyOptimizerVersion(connection, "");
+      connection.createStatement().execute("SET OPTIMIZER_VERSION='LATEST'");
+      verifyOptimizerVersion(connection, "LATEST");
+      try (ResultSet rs = connection.createStatement().executeQuery("SELECT 1")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(1L);
+        assertThat(rs.next()).isFalse();
+      }
+    }
+  }
+
+  @Test
+  public void setInvalidOptimizerVersion() throws SQLException {
+    try (Connection connection = createConnection()) {
+      connection.createStatement().execute("SET OPTIMIZER_VERSION='9999999'");
+      try (ResultSet rs = connection.createStatement().executeQuery("SELECT 1")) {
+        fail("missing expected exception");
+      } catch (SQLException e) {
+        assertThat((Throwable) e).isInstanceOf(JdbcSqlException.class);
+        JdbcSqlException je = (JdbcSqlException) e;
+        assertThat(je.getCode()).isEqualTo(Code.INVALID_ARGUMENT);
+      }
+    }
+  }
+
+  @Test
+  public void optimizerVersionInQueryHint() throws SQLException {
+    try (Connection connection = createConnection()) {
+      verifyOptimizerVersion(connection, "");
+      try (ResultSet rs =
+          connection.createStatement().executeQuery("@{optimizer_version=1} SELECT 1")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(1L);
+        assertThat(rs.next()).isFalse();
+      }
+      try (ResultSet rs =
+          connection.createStatement().executeQuery("@{optimizer_version=latest} SELECT 1")) {
+        assertThat(rs.next()).isTrue();
+        assertThat(rs.getLong(1)).isEqualTo(1L);
+        assertThat(rs.next()).isFalse();
+      }
+    }
+  }
+
+  @Test
+  public void optimizerVersionInEnvironment() throws SQLException {
+    try {
+      SpannerOptions.useEnvironment(
+          new SpannerOptions.SpannerEnvironment() {
+            @Override
+            public String getOptimizerVersion() {
+              return "1";
+            }
+          });
+      try (Connection connection = createConnection()) {
+        // Environment query options are not visible to the connection.
+        verifyOptimizerVersion(connection, "");
+        try (ResultSet rs = connection.createStatement().executeQuery("SELECT 1")) {
+          assertThat(rs.next()).isTrue();
+          assertThat(rs.getLong(1)).isEqualTo(1L);
+          assertThat(rs.next()).isFalse();
+        }
+      }
+      // Close the pool to force re-initialization of any cached connections.
+      SpannerPool.closeSpannerPool();
+
+      // Now set an invalid version on the environment. The query will now fail.
+      SpannerOptions.useEnvironment(
+          new SpannerOptions.SpannerEnvironment() {
+            @Override
+            public String getOptimizerVersion() {
+              return "9999999";
+            }
+          });
+      try (Connection connection = createConnection()) {
+        try (ResultSet rs = connection.createStatement().executeQuery("SELECT 1")) {
+          fail("missing expected exception");
+        } catch (SQLException e) {
+          assertThat((Throwable) e).isInstanceOf(JdbcSqlException.class);
+          JdbcSqlException je = (JdbcSqlException) e;
+          assertThat(je.getCode()).isEqualTo(Code.INVALID_ARGUMENT);
+        }
+      }
+    } finally {
+      SpannerOptions.useDefaultEnvironment();
+    }
+  }
+}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcSqlScriptTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcSqlScriptTest.java
index f8154dc..87f5abe 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcSqlScriptTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcSqlScriptTest.java
@@ -61,6 +61,7 @@ public class ITJdbcSqlScriptTest extends ITAbstractJdbcTest {
       "ITSqlScriptTest_TestAutocommitDmlMode.sql";
   private static final String TEST_AUTOCOMMIT_READ_ONLY =
       "ITSqlScriptTest_TestAutocommitReadOnly.sql";
+  private static final String TEST_QUERY_OPTIONS = "ITSqlScriptTest_TestQueryOptions.sql";
   private static final String TEST_STATEMENT_TIMEOUT = "ITSqlScriptTest_TestStatementTimeout.sql";
   private static final String TEST_SET_STATEMENTS = "ITSqlScriptTest_TestSetStatements.sql";
   private static final String TEST_INVALID_STATEMENTS = "ITSqlScriptTest_TestInvalidStatements.sql";
@@ -208,4 +209,12 @@ public class ITJdbcSqlScriptTest extends ITAbstractJdbcTest {
           JdbcGenericConnection.of(connection), TEST_INVALID_STATEMENTS, SqlScriptVerifier.class);
     }
   }
+
+  @Test
+  public void test14_TestQueryOptions() throws Exception {
+    try (Connection connection = createConnection()) {
+      verifier.verifyStatementsInFile(
+          JdbcGenericConnection.of(connection), TEST_QUERY_OPTIONS, SqlScriptVerifier.class);
+    }
+  }
 }
diff --git a/src/test/resources/com/google/cloud/spanner/jdbc/ClientSideStatementsTest.sql b/src/test/resources/com/google/cloud/spanner/jdbc/ClientSideStatementsTest.sql
index fe8afd8..56d8041 100644
--- a/src/test/resources/com/google/cloud/spanner/jdbc/ClientSideStatementsTest.sql
+++ b/src/test/resources/com/google/cloud/spanner/jdbc/ClientSideStatementsTest.sql
@@ -1931,6 +1931,205 @@ NEW_CONNECTION;
 @EXPECT EXCEPTION INVALID_ARGUMENT
 show variable/-read_only_staleness;
 NEW_CONNECTION;
+show variable optimizer_version;
+NEW_CONNECTION;
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+show variable optimizer_version;
+NEW_CONNECTION;
+   show variable optimizer_version;
+NEW_CONNECTION;
+			show variable optimizer_version;
+NEW_CONNECTION;
+
+
+
+show variable optimizer_version;
+NEW_CONNECTION;
+show variable optimizer_version  ;
+NEW_CONNECTION;
+show variable optimizer_version		;
+NEW_CONNECTION;
+show variable optimizer_version
+
+;
+NEW_CONNECTION;
+show   variable   optimizer_version;
+NEW_CONNECTION;
+show	variable	optimizer_version;
+NEW_CONNECTION;
+show
+variable
+optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+foo show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version bar;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+%show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version%;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable%optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+_show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version_;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable_optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+&show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version&;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable&optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+$show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version$;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable$optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+@show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version@;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable@optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+!show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version!;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable!optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+*show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version*;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable*optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+(show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version(;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable(optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+)show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version);
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable)optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable-optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
++show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version+;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable+optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-#show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version-#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable-#optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable/optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+\show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version\;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable\optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+?show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version?;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable?optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-/show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version-/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable-/optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/#show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version/#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable/#optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/-show variable optimizer_version;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable optimizer_version/-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+show variable/-optimizer_version;
+NEW_CONNECTION;
 begin;
 NEW_CONNECTION;
 BEGIN;
@@ -11076,3 +11275,795 @@ set read_only_staleness='EXACT_STALENESS 9999ns'/-;
 NEW_CONNECTION;
 @EXPECT EXCEPTION INVALID_ARGUMENT
 set read_only_staleness='EXACT_STALENESS/-9999ns';
+NEW_CONNECTION;
+set optimizer_version='1';
+NEW_CONNECTION;
+SET OPTIMIZER_VERSION='1';
+NEW_CONNECTION;
+set optimizer_version='1';
+NEW_CONNECTION;
+   set optimizer_version='1';
+NEW_CONNECTION;
+			set optimizer_version='1';
+NEW_CONNECTION;
+
+
+
+set optimizer_version='1';
+NEW_CONNECTION;
+set optimizer_version='1'  ;
+NEW_CONNECTION;
+set optimizer_version='1'		;
+NEW_CONNECTION;
+set optimizer_version='1'
+
+;
+NEW_CONNECTION;
+set   optimizer_version='1';
+NEW_CONNECTION;
+set	optimizer_version='1';
+NEW_CONNECTION;
+set
+optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+foo set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1' bar;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+%set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'%;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set%optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+_set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'_;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set_optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+&set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'&;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set&optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+$set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'$;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set$optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+@set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'@;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set@optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+!set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'!;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set!optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+*set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'*;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set*optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+(set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'(;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set(optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+)set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1');
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set)optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
++set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'+;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set+optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-#set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'-#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-#optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+\set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'\;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set\optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+?set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'?;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set?optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-/set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'-/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-/optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/#set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'/#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/#optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/-set optimizer_version='1';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='1'/-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/-optimizer_version='1';
+NEW_CONNECTION;
+set optimizer_version='200';
+NEW_CONNECTION;
+SET OPTIMIZER_VERSION='200';
+NEW_CONNECTION;
+set optimizer_version='200';
+NEW_CONNECTION;
+   set optimizer_version='200';
+NEW_CONNECTION;
+			set optimizer_version='200';
+NEW_CONNECTION;
+
+
+
+set optimizer_version='200';
+NEW_CONNECTION;
+set optimizer_version='200'  ;
+NEW_CONNECTION;
+set optimizer_version='200'		;
+NEW_CONNECTION;
+set optimizer_version='200'
+
+;
+NEW_CONNECTION;
+set   optimizer_version='200';
+NEW_CONNECTION;
+set	optimizer_version='200';
+NEW_CONNECTION;
+set
+optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+foo set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200' bar;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+%set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'%;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set%optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+_set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'_;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set_optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+&set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'&;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set&optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+$set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'$;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set$optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+@set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'@;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set@optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+!set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'!;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set!optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+*set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'*;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set*optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+(set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'(;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set(optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+)set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200');
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set)optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
++set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'+;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set+optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-#set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'-#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-#optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+\set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'\;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set\optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+?set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'?;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set?optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-/set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'-/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-/optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/#set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'/#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/#optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/-set optimizer_version='200';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='200'/-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/-optimizer_version='200';
+NEW_CONNECTION;
+set optimizer_version='LATEST';
+NEW_CONNECTION;
+SET OPTIMIZER_VERSION='LATEST';
+NEW_CONNECTION;
+set optimizer_version='latest';
+NEW_CONNECTION;
+   set optimizer_version='LATEST';
+NEW_CONNECTION;
+			set optimizer_version='LATEST';
+NEW_CONNECTION;
+
+
+
+set optimizer_version='LATEST';
+NEW_CONNECTION;
+set optimizer_version='LATEST'  ;
+NEW_CONNECTION;
+set optimizer_version='LATEST'		;
+NEW_CONNECTION;
+set optimizer_version='LATEST'
+
+;
+NEW_CONNECTION;
+set   optimizer_version='LATEST';
+NEW_CONNECTION;
+set	optimizer_version='LATEST';
+NEW_CONNECTION;
+set
+optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+foo set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST' bar;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+%set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'%;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set%optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+_set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'_;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set_optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+&set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'&;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set&optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+$set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'$;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set$optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+@set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'@;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set@optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+!set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'!;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set!optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+*set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'*;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set*optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+(set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'(;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set(optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+)set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST');
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set)optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
++set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'+;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set+optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-#set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'-#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-#optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+\set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'\;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set\optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+?set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'?;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set?optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-/set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'-/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-/optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/#set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'/#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/#optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/-set optimizer_version='LATEST';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='LATEST'/-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/-optimizer_version='LATEST';
+NEW_CONNECTION;
+set optimizer_version='';
+NEW_CONNECTION;
+SET OPTIMIZER_VERSION='';
+NEW_CONNECTION;
+set optimizer_version='';
+NEW_CONNECTION;
+   set optimizer_version='';
+NEW_CONNECTION;
+			set optimizer_version='';
+NEW_CONNECTION;
+
+
+
+set optimizer_version='';
+NEW_CONNECTION;
+set optimizer_version=''  ;
+NEW_CONNECTION;
+set optimizer_version=''		;
+NEW_CONNECTION;
+set optimizer_version=''
+
+;
+NEW_CONNECTION;
+set   optimizer_version='';
+NEW_CONNECTION;
+set	optimizer_version='';
+NEW_CONNECTION;
+set
+optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+foo set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='' bar;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+%set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''%;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set%optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+_set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''_;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set_optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+&set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''&;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set&optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+$set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''$;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set$optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+@set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''@;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set@optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+!set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''!;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set!optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+*set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''*;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set*optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+(set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''(;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set(optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+)set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version='');
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set)optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
++set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''+;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set+optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-#set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''-#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-#optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+\set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''\;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set\optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+?set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''?;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set?optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+-/set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''-/;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set-/optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/#set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''/#;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/#optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+/-set optimizer_version='';
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set optimizer_version=''/-;
+NEW_CONNECTION;
+@EXPECT EXCEPTION INVALID_ARGUMENT
+set/-optimizer_version='';
diff --git a/src/test/resources/com/google/cloud/spanner/jdbc/ConnectionImplGeneratedSqlScriptTest.sql b/src/test/resources/com/google/cloud/spanner/jdbc/ConnectionImplGeneratedSqlScriptTest.sql
index c765085..a4d68cd 100644
--- a/src/test/resources/com/google/cloud/spanner/jdbc/ConnectionImplGeneratedSqlScriptTest.sql
+++ b/src/test/resources/com/google/cloud/spanner/jdbc/ConnectionImplGeneratedSqlScriptTest.sql
@@ -2,6 +2,16 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -142,15 +152,15 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:54.904000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:54.904000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:57.888000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:57.888000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:54.904000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:57.888000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
@@ -179,6 +189,26 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
@@ -272,6 +302,16 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -422,15 +462,15 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:54.986000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:54.986000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.128000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.128000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:54.986000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:54.986000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.128000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.128000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
@@ -461,6 +501,26 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
@@ -561,6 +621,16 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -711,15 +781,15 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.049000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.049000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.214000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.214000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.049000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.049000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.214000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.214000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
@@ -750,6 +820,26 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
@@ -850,6 +940,16 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SET READ_ONLY_STALENESS='MAX_STALENESS 10s';
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SET READ_ONLY_STALENESS='MAX_STALENESS 10s';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SET READ_ONLY_STALENESS='MAX_STALENESS 10s';
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -1000,15 +1100,15 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SET READ_ONLY_STALENESS='MAX_STALENESS 10s';
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.100000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.100000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.271000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.271000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SET READ_ONLY_STALENESS='MAX_STALENESS 10s';
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.100000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.100000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.271000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.271000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
@@ -1039,6 +1139,26 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
 SET READ_ONLY_STALENESS='MAX_STALENESS 10s';
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SET READ_ONLY_STALENESS='MAX_STALENESS 10s';
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+SET READ_ONLY_STALENESS='MAX_STALENESS 10s';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
@@ -1141,6 +1261,20 @@ SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 SELECT 1 AS TEST;
 COMMIT;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+SELECT 1 AS TEST;
+COMMIT;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+SELECT 1 AS TEST;
+COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -1345,8 +1479,8 @@ SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 SELECT 1 AS TEST;
 COMMIT;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.147000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.147000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.339000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.339000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
@@ -1354,8 +1488,8 @@ SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 SELECT 1 AS TEST;
 COMMIT;
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.147000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.147000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.339000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.339000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
@@ -1396,6 +1530,30 @@ SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 SELECT 1 AS TEST;
 COMMIT;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+SELECT 1 AS TEST;
+COMMIT;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+SELECT 1 AS TEST;
+COMMIT;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
@@ -1515,6 +1673,14 @@ COMMIT;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -1638,14 +1804,14 @@ SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.196000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.196000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.423000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.423000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.196000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.196000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.423000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.423000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
@@ -1671,6 +1837,24 @@ SET AUTOCOMMIT=TRUE;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=TRUE;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=TRUE;
@@ -1762,6 +1946,18 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 SET TRANSACTION READ ONLY;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+SET TRANSACTION READ ONLY;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+SET TRANSACTION READ ONLY;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -1928,8 +2124,8 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 SET TRANSACTION READ ONLY;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.237000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.237000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.470000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.470000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -1937,7 +2133,7 @@ SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 SET TRANSACTION READ ONLY;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.237000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.470000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -1971,6 +2167,28 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 SET TRANSACTION READ ONLY;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+SET TRANSACTION READ ONLY;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+SET TRANSACTION READ ONLY;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -2074,6 +2292,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -2218,15 +2446,15 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.284000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.284000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.514000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.514000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.284000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.514000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -2255,6 +2483,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -2348,6 +2596,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -2512,15 +2770,15 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.377000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.377000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.580000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.580000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.377000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.377000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.580000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.580000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -2551,6 +2809,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -2651,6 +2929,18 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -2842,16 +3132,16 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.430000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.430000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.663000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.663000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.430000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.430000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.663000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.663000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -2887,6 +3177,28 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -2996,6 +3308,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -3160,15 +3482,15 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.491000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.491000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.751000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.751000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.491000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.491000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.751000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.751000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -3199,6 +3521,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 SELECT 1 AS TEST;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+SELECT 1 AS TEST;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -3298,6 +3640,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 START BATCH DDL;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+START BATCH DDL;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+START BATCH DDL;
 ABORT BATCH;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -3439,13 +3791,13 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 START BATCH DDL;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.524000000Z';
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.797000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 START BATCH DDL;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.524000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.797000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -3472,6 +3824,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 START BATCH DDL;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+START BATCH DDL;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+START BATCH DDL;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -3567,6 +3939,20 @@ SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 UPDATE foo SET bar=1;
 COMMIT;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+UPDATE foo SET bar=1;
+COMMIT;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+UPDATE foo SET bar=1;
+COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -3785,8 +4171,8 @@ SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 UPDATE foo SET bar=1;
 COMMIT;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.594000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.594000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.850000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.850000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -3794,8 +4180,8 @@ SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 UPDATE foo SET bar=1;
 COMMIT;
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.594000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.594000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.850000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.850000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -3836,6 +4222,30 @@ SET AUTOCOMMIT=TRUE;
 BEGIN TRANSACTION;
 UPDATE foo SET bar=1;
 COMMIT;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+UPDATE foo SET bar=1;
+COMMIT;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+BEGIN TRANSACTION;
+UPDATE foo SET bar=1;
+COMMIT;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -3955,6 +4365,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 UPDATE foo SET bar=1;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+UPDATE foo SET bar=1;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+UPDATE foo SET bar=1;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -4119,15 +4539,15 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 UPDATE foo SET bar=1;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.650000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.650000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.947000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.947000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 UPDATE foo SET bar=1;
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.650000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.650000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.947000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.947000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -4158,6 +4578,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
 UPDATE foo SET bar=1;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+UPDATE foo SET bar=1;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+UPDATE foo SET bar=1;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -4256,6 +4696,14 @@ UPDATE foo SET bar=1;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -4393,14 +4841,14 @@ SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.687000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.687000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:58.985000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:58.985000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.687000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2019-07-31T10:46:55.687000000Z'
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:58.985000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','MIN_READ_TIMESTAMP 2020-03-05T17:56:58.985000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -4426,6 +4874,24 @@ SET AUTOCOMMIT=TRUE;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=TRUE;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=TRUE;
@@ -4515,6 +4981,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 BEGIN TRANSACTION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -4659,15 +5135,15 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 BEGIN TRANSACTION;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.716000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.716000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.013000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.013000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 BEGIN TRANSACTION;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.716000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.013000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -4696,6 +5172,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 BEGIN TRANSACTION;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -4792,6 +5288,22 @@ SET TRANSACTION READ ONLY;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 COMMIT;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET TRANSACTION READ ONLY;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET TRANSACTION READ ONLY;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -5031,8 +5543,8 @@ SET TRANSACTION READ ONLY;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 COMMIT;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.754000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.754000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.061000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.061000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -5042,7 +5554,7 @@ SET TRANSACTION READ ONLY;
 SELECT 1 AS TEST;
 COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.754000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.061000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -5086,6 +5598,32 @@ SET TRANSACTION READ ONLY;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 COMMIT;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET TRANSACTION READ ONLY;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET TRANSACTION READ ONLY;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -5214,6 +5752,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 COMMIT;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+COMMIT;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -5372,15 +5920,15 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 COMMIT;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.806000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.806000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.106000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.106000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.806000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.106000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -5409,6 +5957,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 COMMIT;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+COMMIT;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+COMMIT;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -5509,6 +6077,20 @@ SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 RUN BATCH;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+RUN BATCH;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+RUN BATCH;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -5721,8 +6303,8 @@ SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 RUN BATCH;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.836000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.836000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.155000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.155000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -5731,7 +6313,7 @@ START BATCH DDL;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 RUN BATCH;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.836000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.155000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -5770,6 +6352,30 @@ SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 RUN BATCH;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+RUN BATCH;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+RUN BATCH;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -5891,6 +6497,22 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 ROLLBACK;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+ROLLBACK;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+ROLLBACK;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -6130,8 +6752,8 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 ROLLBACK;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.874000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.874000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.218000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.218000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -6141,7 +6763,7 @@ BEGIN TRANSACTION;
 SELECT 1 AS TEST;
 ROLLBACK;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.874000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.218000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -6185,6 +6807,32 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 ROLLBACK;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+ROLLBACK;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+ROLLBACK;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -6314,6 +6962,18 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -6487,14 +7147,14 @@ SET AUTOCOMMIT=FALSE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.915000000Z';
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.279000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.915000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.279000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -6526,6 +7186,28 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -6629,6 +7311,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SELECT 1 AS TEST;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -6775,13 +7467,13 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.970000000Z';
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.328000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.970000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.328000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -6808,6 +7500,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SELECT 1 AS TEST;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SELECT 1 AS TEST;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -6901,6 +7613,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -7059,15 +7781,15 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:55.995000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:55.995000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.396000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.396000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:55.995000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.396000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -7096,6 +7818,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -7194,6 +7936,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SET TRANSACTION READ ONLY;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET TRANSACTION READ ONLY;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET TRANSACTION READ ONLY;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -7333,15 +8085,15 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SET TRANSACTION READ ONLY;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.013000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:56.013000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.425000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.425000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SET TRANSACTION READ ONLY;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.013000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.425000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -7370,6 +8122,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 SET TRANSACTION READ ONLY;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET TRANSACTION READ ONLY;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+SET TRANSACTION READ ONLY;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -7463,6 +8235,16 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
 ABORT BATCH;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -7604,13 +8386,13 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.029000000Z';
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.451000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.029000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.451000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -7637,6 +8419,26 @@ NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -7733,6 +8535,22 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 COMMIT;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -7972,8 +8790,8 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 COMMIT;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.066000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:56.066000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.493000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.493000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -7983,7 +8801,7 @@ BEGIN TRANSACTION;
 SELECT 1 AS TEST;
 COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.066000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.493000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -8027,6 +8845,32 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 COMMIT;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -8156,6 +9000,18 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 ABORT BATCH;
 NEW_CONNECTION;
 SET READONLY=FALSE;
@@ -8324,14 +9180,14 @@ SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.104000000Z';
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.539000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.104000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.539000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -8363,6 +9219,28 @@ SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 START BATCH DDL;
 CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+START BATCH DDL;
+CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -8465,6 +9343,14 @@ CREATE TABLE foo (id INT64 NOT NULL, name STRING(100)) PRIMARY KEY (id);
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -8596,14 +9482,14 @@ SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.132000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:56.132000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.575000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.575000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.132000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.575000000Z';
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -8627,6 +9513,24 @@ SET AUTOCOMMIT=FALSE;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
+NEW_CONNECTION;
+SET READONLY=FALSE;
+SET AUTOCOMMIT=FALSE;
 NEW_CONNECTION;
 SET READONLY=FALSE;
 SET AUTOCOMMIT=FALSE;
@@ -8715,6 +9619,16 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 BEGIN TRANSACTION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -8855,15 +9769,15 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 BEGIN TRANSACTION;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.157000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:56.157000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.601000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.601000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 BEGIN TRANSACTION;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.157000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.601000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -8892,6 +9806,26 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 BEGIN TRANSACTION;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -8985,6 +9919,16 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 COMMIT;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+COMMIT;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -9133,15 +10077,15 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 COMMIT;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.175000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:56.175000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.621000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.621000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.175000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.621000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -9170,6 +10114,26 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 COMMIT;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+COMMIT;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+COMMIT;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -9272,6 +10236,22 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 ROLLBACK;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+ROLLBACK;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+ROLLBACK;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -9501,8 +10481,8 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 ROLLBACK;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.203000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:56.203000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.654000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.654000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
@@ -9512,7 +10492,7 @@ BEGIN TRANSACTION;
 SELECT 1 AS TEST;
 ROLLBACK;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.203000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.654000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -9556,6 +10536,32 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 ROLLBACK;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+ROLLBACK;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+ROLLBACK;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -9686,6 +10692,18 @@ SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -9854,14 +10872,14 @@ SET AUTOCOMMIT=FALSE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.262000000Z';
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.711000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.262000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.711000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -9893,6 +10911,28 @@ SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -9996,6 +11036,16 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+SELECT 1 AS TEST;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -10137,13 +11187,13 @@ SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.291000000Z';
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.762000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 SELECT 1 AS TEST;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.291000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.762000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -10170,6 +11220,26 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 SELECT 1 AS TEST;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+SELECT 1 AS TEST;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+SELECT 1 AS TEST;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -10263,6 +11333,16 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -10411,15 +11491,15 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.311000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:56.311000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.805000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.805000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.311000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.805000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -10448,6 +11528,26 @@ NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+SET READ_ONLY_STALENESS='EXACT_STALENESS 10s';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -10550,6 +11650,22 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 COMMIT;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -10779,8 +11895,8 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 COMMIT;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.334000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:56.334000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.849000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.849000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
@@ -10790,7 +11906,7 @@ BEGIN TRANSACTION;
 SELECT 1 AS TEST;
 COMMIT;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.334000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.849000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -10834,6 +11950,32 @@ BEGIN TRANSACTION;
 @EXPECT RESULT_SET 'TEST',1
 SELECT 1 AS TEST;
 COMMIT;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+BEGIN TRANSACTION;
+@EXPECT RESULT_SET 'TEST',1
+SELECT 1 AS TEST;
+COMMIT;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -10962,6 +12104,14 @@ COMMIT;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION'
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
 @EXPECT EXCEPTION FAILED_PRECONDITION
 ABORT BATCH;
 NEW_CONNECTION;
@@ -11083,14 +12233,14 @@ SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
-SET READ_ONLY_STALENESS='READ_TIMESTAMP 2019-07-31T10:46:56.355000000Z';
-@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2019-07-31T10:46:56.355000000Z'
+SET READ_ONLY_STALENESS='READ_TIMESTAMP 2020-03-05T17:56:59.879000000Z';
+@EXPECT RESULT_SET 'READ_ONLY_STALENESS','READ_TIMESTAMP 2020-03-05T17:56:59.879000000Z'
 SHOW VARIABLE READ_ONLY_STALENESS;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
 @EXPECT EXCEPTION FAILED_PRECONDITION
-SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2019-07-31T10:46:56.355000000Z';
+SET READ_ONLY_STALENESS='MIN_READ_TIMESTAMP 2020-03-05T17:56:59.879000000Z';
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
@@ -11114,6 +12264,24 @@ SET AUTOCOMMIT=FALSE;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
+SET OPTIMIZER_VERSION='1';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='2';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','2'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='latest';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','latest'
+SHOW VARIABLE OPTIMIZER_VERSION;
+SET OPTIMIZER_VERSION='';
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
+NEW_CONNECTION;
+SET READONLY=TRUE;
+SET AUTOCOMMIT=FALSE;
 NEW_CONNECTION;
 SET READONLY=TRUE;
 SET AUTOCOMMIT=FALSE;
diff --git a/src/test/resources/com/google/cloud/spanner/jdbc/ITSqlScriptTest_TestQueryOptions.sql b/src/test/resources/com/google/cloud/spanner/jdbc/ITSqlScriptTest_TestQueryOptions.sql
new file mode 100644
index 0000000..923cc97
--- /dev/null
+++ b/src/test/resources/com/google/cloud/spanner/jdbc/ITSqlScriptTest_TestQueryOptions.sql
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test setting and getting the optimizer version to use.
+ */
+
+-- Set and get valid values.
+@EXPECT NO_RESULT
+SET OPTIMIZER_VERSION = '1';
+
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','1'
+SHOW VARIABLE OPTIMIZER_VERSION;
+
+@EXPECT NO_RESULT
+SET OPTIMIZER_VERSION = '555';
+
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','555'
+SHOW VARIABLE OPTIMIZER_VERSION;
+
+@EXPECT NO_RESULT
+SET OPTIMIZER_VERSION = 'LATEST';
+
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION','LATEST'
+SHOW VARIABLE OPTIMIZER_VERSION;
+
+@EXPECT NO_RESULT
+SET OPTIMIZER_VERSION = '';
+
+@EXPECT RESULT_SET 'OPTIMIZER_VERSION',''
+SHOW VARIABLE OPTIMIZER_VERSION;
+
+-- Try to set invalid values.
+@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';