Repository: googleapis/java-spanner-jdbc
Pull request #76 (2020-03-05 19:03:06)

feat: add support for QueryOptions
Adds the ability to set `QueryOptions` when running Cloud Spanner queries through JDBC. For now, only setting the `query_optimizer_version` is added.

QueryOptions can be configured through the following mechanisms:

1. In the connection URL of a JDBC connection using the property `optimizerVersion`. The version specified here will be applied to all queries executed by the connection.
1. By executing the statement `SET OPTIMIZER_VERSION='<version>'` on a JDBC connection. All queries that are executed after this statement will use the version specified.
1. Through the `SPANNER_OPTIMIZER_VERSION` environment variable.

If the options are configured through multiple mechanisms then:

1. Options set at the JDBC connection URL level will override options configured at the environment variable level.
1. Options set by executing a `SET OPTIMIZER_VERSION='<version>'` will override options set at a JDBC connection URL level or environment variable level.

If no options are set, the optimizer version will default to:

1. The optimizer version the database is pinned to.
1. If the database is not pinned to a specific version, then the Cloud Spanner backend will use the "latest" version.

Depends on changes in the client library that have not yet been merged, meaning that the build of this PR will fail until the required changes have been merged.

olavloite: > LGTM with some minor comments.
> 
> One request, if you could update the description of this PR to contain the information about the env variable and the precedence, that would be great. Example [PR](https://github.com/googleapis/google-cloud-dotnet/pull/4512)

Done.
skuruppu: I merged in the [v1.51.0](https://github.com/googleapis/java-spanner/pull/96) release which should hopefully be available on Maven by the end of your day.
codecov[bot]: # [Codecov](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76?src=pr&el=h1) Report
> Merging [#76](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76?src=pr&el=desc) into [master](https://codecov.io/gh/googleapis/java-spanner-jdbc/commit/a5e031d3183f8fe88a621500f235ca2b0242f50b?src=pr&el=desc) will **increase** coverage by `0.01%`.
> The diff coverage is `84.61%`.

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

```diff
@@             Coverage Diff              @@
##             master      #76      +/-   ##
============================================
+ Coverage     77.82%   77.84%   +0.01%     
- Complexity     1724     1732       +8     
============================================
  Files            60       60              
  Lines          5836     5881      +45     
  Branches        769      779      +10     
============================================
+ Hits           4542     4578      +36     
- Misses          922      925       +3     
- Partials        372      378       +6
```


| [Impacted Files](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76?src=pr&el=tree) | Coverage Δ | Complexity Δ | |
|---|---|---|---|
| [...java/com/google/cloud/spanner/jdbc/JdbcDriver.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9KZGJjRHJpdmVyLmphdmE=) | `26.86% <ø> (ø)` | `7 <0> (ø)` | :arrow_down: |
| [...anner/jdbc/ClientSideStatementValueConverters.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9DbGllbnRTaWRlU3RhdGVtZW50VmFsdWVDb252ZXJ0ZXJzLmphdmE=) | `93.47% <100%> (+0.21%)` | `0 <0> (ø)` | :arrow_down: |
| [...m/google/cloud/spanner/jdbc/ConnectionOptions.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9Db25uZWN0aW9uT3B0aW9ucy5qYXZh) | `85.16% <100%> (+0.59%)` | `57 <3> (+3)` | :arrow_up: |
| [...com/google/cloud/spanner/jdbc/StatementResult.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9TdGF0ZW1lbnRSZXN1bHQuamF2YQ==) | `100% <100%> (ø)` | `0 <0> (ø)` | :arrow_down: |
| [.../spanner/jdbc/ConnectionStatementExecutorImpl.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9Db25uZWN0aW9uU3RhdGVtZW50RXhlY3V0b3JJbXBsLmphdmE=) | `100% <100%> (ø)` | `29 <2> (+2)` | :arrow_up: |
| [...com/google/cloud/spanner/jdbc/StatementParser.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9TdGF0ZW1lbnRQYXJzZXIuamF2YQ==) | `86.59% <76%> (-1.39%)` | `48 <1> (+2)` | |
| [.../com/google/cloud/spanner/jdbc/ConnectionImpl.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9Db25uZWN0aW9uSW1wbC5qYXZh) | `82.41% <80%> (-0.14%)` | `157 <2> (+2)` | |
| [...oogle/cloud/spanner/jdbc/SingleUseTransaction.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9TaW5nbGVVc2VUcmFuc2FjdGlvbi5qYXZh) | `86% <0%> (-1%)` | `36% <0%> (-1%)` | |
| [...ud/spanner/jdbc/AbstractJdbcPreparedStatement.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9BYnN0cmFjdEpkYmNQcmVwYXJlZFN0YXRlbWVudC5qYXZh) | `90.18% <0%> (ø)` | `52% <0%> (ø)` | :arrow_down: |
| [...a/com/google/cloud/spanner/jdbc/JdbcResultSet.java](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvamRiYy9KZGJjUmVzdWx0U2V0LmphdmE=) | `81.08% <0%> (ø)` | `129% <0%> (ø)` | :arrow_down: |
| ... and [1 more](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76?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/76?src=pr&el=footer). Last update [a5e031d...8cf99be](https://codecov.io/gh/googleapis/java-spanner-jdbc/pull/76?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

skuruppu: Looks like I can't merge in #75 until #73 can be merged in but that can only be done once googleapis/java-core#179 is in a release. Yikes, so many dependencies!
skuruppu: I don't understand this failure in the [Linkage Monitor](https://source.cloud.google.com/results/invocations/1214d3a9-1878-41f3-b624-8827ae45181f/targets/cloud-devrel%2Fclient-libraries%2Fjava%2Fjava-spanner-jdbc%2Fpresubmit%2Flinkage-monitor/log) when all other tests pass:
```
Mar 17, 2020 2:24:16 AM com.google.cloud.tools.dependencies.linkagemonitor.LinkageMonitor run
INFO: BOM Coordinates: com.google.cloud:libraries-bom:4.2.0
Mar 17, 2020 2:27:17 AM com.google.cloud.tools.dependencies.linkagemonitor.LinkageMonitor run
SEVERE: Newly introduced problems:
Class com.google.spanner.v1.ExecuteSqlRequest$QueryOptions is not found
  referenced from com.google.cloud.spanner.jdbc.ConnectionImpl (google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar)
  referenced from com.google.cloud.spanner.jdbc.ConnectionOptions (google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar)
  referenced from com.google.cloud.spanner.jdbc.StatementParser (google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar)
Class com.google.spanner.v1.ExecuteSqlRequest$QueryOptions$Builder is not found
  referenced from com.google.cloud.spanner.jdbc.ConnectionImpl (google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar)
  referenced from com.google.cloud.spanner.jdbc.ConnectionOptions (google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar)
  referenced from com.google.cloud.spanner.jdbc.StatementParser (google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar)
(google-cloud-spanner-1.49.2.jar) com.google.cloud.spanner.Statement's method getQueryOptions() is not found
  referenced from com.google.cloud.spanner.jdbc.StatementParser (google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar)
(google-cloud-spanner-1.49.2.jar) com.google.cloud.spanner.Statement's method toBuilder() is not found
  referenced from com.google.cloud.spanner.jdbc.StatementParser (google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar)
(google-cloud-spanner-1.49.2.jar) com.google.cloud.spanner.Statement$Builder's method withQueryOptions(com.google.spanner.v1.ExecuteSqlRequest$QueryOptions arg1) is not found
  referenced from com.google.cloud.spanner.jdbc.StatementParser (google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar)

google-cloud-spanner-jdbc-1.13.1-SNAPSHOT.jar is at:
  com.google.cloud:google-cloud-spanner-jdbc:1.13.1-SNAPSHOT (compile)
google-cloud-spanner-1.49.2.jar is at:
  com.google.cloud:google-cloud-spanner:1.49.2 (compile)

Mar 17, 2020 2:27:17 AM com.google.cloud.tools.dependencies.linkagemonitor.LinkageMonitor main
SEVERE: Found 5 new linkage errors
```
gcf-merge-on-green[bot]: Your PR has attempted to merge for 3 hours. Please check that all required checks have passed, you have an automerge label, and that all your reviewers have approved the PR
gcf-merge-on-green[bot]: Your PR has attempted to merge for 3 hours. Please check that all required checks have passed, you have an automerge label, and that all your reviewers have approved the PR
gcf-merge-on-green[bot]: Your PR has attempted to merge for 3 hours. Please check that all required checks have passed, you have an automerge label, and that all your reviewers have approved the PR
gcf-merge-on-green[bot]: Your PR has attempted to merge for 3 hours. Please check that all required checks have passed, you have an automerge label, and that all your reviewers have approved the PR
hengfengli: @skuruppu I keep receiving notification emails due to the label `automerge`. I just removed it at the moment. 
gcf-merge-on-green[bot]: Your PR was not mergeable because either one of your required status checks failed, or one of your required reviews was not approved. See required reviews for your repo here: https://github.com/googleapis/sloth/blob/master/required-checks.json
olavloite: I don't understand this Linkage Monitor error either. I just tried to execute it locally and it executed without any problems. I also don't understand why the log seems to indicate that Cloud Spanner 1.49.2 is being used, while it clearly uses 1.51.0. The output locally was:

```
loite@loite-XPS-15-9575:~/wsgoogle/java-spanner-jdbc$ mv ~/Downloads/linkage-monitor-latest-all-deps.jar .
loite@loite-XPS-15-9575:~/wsgoogle/java-spanner-jdbc$ java -jar linkage-monitor-latest-all-deps.jar com.google.cloud:libraries-bom
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Mar 17, 2020 2:09:30 PM com.google.cloud.tools.dependencies.linkagemonitor.LinkageMonitor run
INFO: BOM Coordinates: com.google.cloud:libraries-bom:4.2.0
Mar 17, 2020 2:12:29 PM com.google.cloud.tools.dependencies.linkagemonitor.LinkageMonitor run
INFO: Snapshot versions have the same 816 errors as baseline
Mar 17, 2020 2:12:29 PM com.google.cloud.tools.dependencies.linkagemonitor.LinkageMonitor main
INFO: No new problem found
```


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3fbddec..d3d90e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,38 @@
 # Changelog
 
+## [1.14.0](https://www.github.com/googleapis/java-spanner-jdbc/compare/v1.13.0...v1.14.0) (2020-03-18)
+
+
+### Features
+
+* add support for foreign keys ([#78](https://www.github.com/googleapis/java-spanner-jdbc/issues/78)) ([9e770f2](https://www.github.com/googleapis/java-spanner-jdbc/commit/9e770f281c03a1e9c034e5ff3ddee44fa20a7b30)), closes [#77](https://www.github.com/googleapis/java-spanner-jdbc/issues/77)
+
+
+### Bug Fixes
+
+* add missing netty-shaded lib for über-jar ([#80](https://www.github.com/googleapis/java-spanner-jdbc/issues/80)) ([3d6f356](https://www.github.com/googleapis/java-spanner-jdbc/commit/3d6f35669671194e6772fe327ce48f27e5bf4643))
+* fix deprecation warnings in JDBC (test) files ([#81](https://www.github.com/googleapis/java-spanner-jdbc/issues/81)) ([a5e031d](https://www.github.com/googleapis/java-spanner-jdbc/commit/a5e031d3183f8fe88a621500f235ca2b0242f50b))
+* include Spanner gRPC test dependencies ([#63](https://www.github.com/googleapis/java-spanner-jdbc/issues/63)) ([a34bfc0](https://www.github.com/googleapis/java-spanner-jdbc/commit/a34bfc0ff1c2ddeef077dbfae4c56bdd53febcb2))
+
+
+### Dependencies
+
+* update core dependencies ([1ae098e](https://www.github.com/googleapis/java-spanner-jdbc/commit/1ae098e924c2a488cfddd0a3aee9511274b7a515))
+* update core dependencies ([#40](https://www.github.com/googleapis/java-spanner-jdbc/issues/40)) ([18c3a1b](https://www.github.com/googleapis/java-spanner-jdbc/commit/18c3a1b069cb507a91d0320e64a8bf8ae8efe394))
+* update core dependencies ([#73](https://www.github.com/googleapis/java-spanner-jdbc/issues/73)) ([cfa1539](https://www.github.com/googleapis/java-spanner-jdbc/commit/cfa153997599c36f1243e87f1ea0760694657dfe))
+* update core dependencies to v1.27.1 ([#61](https://www.github.com/googleapis/java-spanner-jdbc/issues/61)) ([181991b](https://www.github.com/googleapis/java-spanner-jdbc/commit/181991bda1f66de707d27dad9658b9177626595a))
+* update core dependencies to v1.27.2 ([#71](https://www.github.com/googleapis/java-spanner-jdbc/issues/71)) ([12425fc](https://www.github.com/googleapis/java-spanner-jdbc/commit/12425fcb4382449e4a7a0edad4c812b7ce15aa71))
+* update core dependencies to v1.54.0 ([#72](https://www.github.com/googleapis/java-spanner-jdbc/issues/72)) ([5676021](https://www.github.com/googleapis/java-spanner-jdbc/commit/567602177e05fa198eaa011fbca05cfe4b72fb13))
+* update core dependencies to v1.92.5 ([#53](https://www.github.com/googleapis/java-spanner-jdbc/issues/53)) ([604ee2b](https://www.github.com/googleapis/java-spanner-jdbc/commit/604ee2b75204ad52eaf724c3fb71e8c13540af7c))
+* update core transport dependencies to v1.34.1 ([#43](https://www.github.com/googleapis/java-spanner-jdbc/issues/43)) ([2b6f04d](https://www.github.com/googleapis/java-spanner-jdbc/commit/2b6f04da3aeebac778fb664c4564fb8b58bf3be4))
+* update core transport dependencies to v1.34.2 ([#62](https://www.github.com/googleapis/java-spanner-jdbc/issues/62)) ([8739015](https://www.github.com/googleapis/java-spanner-jdbc/commit/8739015f62289adb92fd55b19a5bff8762da20a9))
+* update dependency com.google.api-client:google-api-client-bom to v1.30.8 ([#46](https://www.github.com/googleapis/java-spanner-jdbc/issues/46)) ([ef891b0](https://www.github.com/googleapis/java-spanner-jdbc/commit/ef891b000045d1f39f91b6a0ed3abaab19c5f05e))
+* update dependency com.google.api-client:google-api-client-bom to v1.30.9 ([#74](https://www.github.com/googleapis/java-spanner-jdbc/issues/74)) ([3b62299](https://www.github.com/googleapis/java-spanner-jdbc/commit/3b622999b9f9645a6086e5efd3206f4d7b0806bc))
+* update dependency com.google.truth:truth to v1.0.1 ([#32](https://www.github.com/googleapis/java-spanner-jdbc/issues/32)) ([5205863](https://www.github.com/googleapis/java-spanner-jdbc/commit/52058636e10951e883523204f0f161db8a972d62))
+* update protobuf.version to v3.11.3 ([#48](https://www.github.com/googleapis/java-spanner-jdbc/issues/48)) ([0779fcb](https://www.github.com/googleapis/java-spanner-jdbc/commit/0779fcb0bfe935c3c302fa8442f733c7e3629761))
+* update protobuf.version to v3.11.4 ([#64](https://www.github.com/googleapis/java-spanner-jdbc/issues/64)) ([f485cff](https://www.github.com/googleapis/java-spanner-jdbc/commit/f485cfffa0de27ce35f5d16c689c31c6ea22138e))
+* update spanner.version to v1.51.0 ([#75](https://www.github.com/googleapis/java-spanner-jdbc/issues/75)) ([4fff168](https://www.github.com/googleapis/java-spanner-jdbc/commit/4fff168eae61fb55933cf3afd67f24ca65dfde54))
+
 ## [1.13.0](https://www.github.com/googleapis/java-spanner-jdbc/compare/1.12.0...v1.13.0) (2020-01-28)
 
 
diff --git a/README.md b/README.md
index db50ef1..4a14e5b 100644
--- a/README.md
+++ b/README.md
@@ -11,16 +11,16 @@ If you are using Maven, add this to your pom.xml file
 <dependency>
   <groupId>com.google.cloud</groupId>
   <artifactId>google-cloud-spanner-jdbc</artifactId>
-  <version>1.13.0</version>
+  <version>1.14.0</version>
 </dependency>
 ```
 If you are using Gradle, add this to your dependencies
 ```Groovy
-compile 'com.google.cloud:google-cloud-spanner-jdbc:1.13.0'
+compile 'com.google.cloud:google-cloud-spanner-jdbc:1.14.0'
 ```
 If you are using SBT, add this to your dependencies
 ```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "1.13.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "1.14.0"
 ```
 [//]: # ({x-version-update-end})
 
diff --git a/pom.xml b/pom.xml
index c78553f..b850ccf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>google-cloud-spanner-jdbc</artifactId>
-  <version>1.13.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner-jdbc:current} -->
+  <version>1.14.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner-jdbc:current} -->
   <packaging>jar</packaging>
   <name>Google Cloud Spanner JDBC</name>
   <url>https://github.com/googleapis/java-spanner-jdbc</url>
@@ -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>
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
new file mode 100644
index 0000000..6f60349
--- /dev/null
+++ b/samples/install-without-bom/pom.xml
@@ -0,0 +1,84 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.google.cloud</groupId>
+  <artifactId>spanner-jdbc-install-without-bom</artifactId>
+  <packaging>jar</packaging>
+  <name>Google Google Cloud Spanner JDBC Install Without Bom</name>
+  <url>https://github.com/googleapis/java-spanner-jdbc</url>
+
+  <!--
+    The parent pom defines common style checks and testing strategies for our samples.
+    Removing or replacing it should not affect the execution of the samples in anyway.
+  -->
+  <parent>
+    <groupId>com.google.cloud.samples</groupId>
+    <artifactId>shared-configuration</artifactId>
+    <version>1.0.12</version>
+  </parent>
+
+  <properties>
+    <maven.compiler.target>1.8</maven.compiler.target>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+
+  <dependencies>
+    <!-- [START spanner-jdbc_install_without_bom] -->
+    <dependency>
+      <groupId>com.google.cloud</groupId>
+      <artifactId>google-cloud-spanner-jdbc</artifactId>
+      <version></version>
+    </dependency>
+    <!-- [END spanner-jdbc_install_without_bom] -->
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.13</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.truth</groupId>
+      <artifactId>truth</artifactId>
+      <version>1.0.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <!-- compile and run all snippet tests -->
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>3.1.0</version>
+        <executions>
+          <execution>
+            <id>add-snippets-source</id>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>../snippets/src/main/java</source>
+              </sources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>add-snippets-tests</id>
+            <goals>
+              <goal>add-test-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>../snippets/src/test/java</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/samples/pom.xml b/samples/pom.xml
new file mode 100644
index 0000000..48df328
--- /dev/null
+++ b/samples/pom.xml
@@ -0,0 +1,56 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.google.cloud</groupId>
+  <artifactId>google-cloud-spanner-jdbc-samples</artifactId>
+  <version>0.0.1-SNAPSHOT</version><!-- This artifact should not be released -->
+  <packaging>pom</packaging>
+  <name>Google Google Cloud Spanner JDBC Samples Parent</name>
+  <url>https://github.com/googleapis/java-spanner-jdbc</url>
+  <description>
+    Java idiomatic client for Google Cloud Platform services.
+  </description>
+
+  <!--
+    The parent pom defines common style checks and testing strategies for our samples.
+    Removing or replacing it should not affect the execution of the samples in anyway.
+  -->
+  <parent>
+    <groupId>com.google.cloud.samples</groupId>
+    <artifactId>shared-configuration</artifactId>
+    <version>1.0.12</version>
+  </parent>
+
+  <properties>
+    <maven.compiler.target>1.8</maven.compiler.target>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <modules>
+    <module>install-without-bom</module>
+    <module>snapshot</module>
+    <module>snippets</module>
+  </modules>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <version>2.8.2</version>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.sonatype.plugins</groupId>
+        <artifactId>nexus-staging-maven-plugin</artifactId>
+        <version>1.6.8</version>
+        <configuration>
+          <skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project> 
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
new file mode 100644
index 0000000..a1b0960
--- /dev/null
+++ b/samples/snapshot/pom.xml
@@ -0,0 +1,83 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.google.cloud</groupId>
+  <artifactId>spanner-jdbc-snapshot</artifactId>
+  <packaging>jar</packaging>
+  <name>Google Google Cloud Spanner JDBC Snapshot Samples</name>
+  <url>https://github.com/googleapis/java-spanner-jdbc</url>
+
+  <!--
+    The parent pom defines common style checks and testing strategies for our samples.
+    Removing or replacing it should not affect the execution of the samples in anyway.
+  -->
+  <parent>
+    <groupId>com.google.cloud.samples</groupId>
+    <artifactId>shared-configuration</artifactId>
+    <version>1.0.12</version>
+  </parent>
+
+  <properties>
+    <maven.compiler.target>1.8</maven.compiler.target>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <!-- {x-version-update-start::current} -->
+  <dependencies>
+    <dependency>
+      <groupId>com.google.cloud</groupId>
+      <artifactId>google-cloud-spanner-jdbc</artifactId>
+      <version></version>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.13</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.truth</groupId>
+      <artifactId>truth</artifactId>
+      <version>1.0.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <!-- {x-version-update-end} -->
+
+  <!-- compile and run all snippet tests -->
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>3.1.0</version>
+        <executions>
+          <execution>
+            <id>add-snippets-source</id>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>../snippets/src/main/java</source>
+              </sources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>add-snippets-tests</id>
+            <goals>
+              <goal>add-test-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>../snippets/src/test/java</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
new file mode 100644
index 0000000..013fcbe
--- /dev/null
+++ b/samples/snippets/pom.xml
@@ -0,0 +1,60 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.google.cloud</groupId>
+  <artifactId>spanner-jdbc-snippets</artifactId>
+  <packaging>jar</packaging>
+  <name>Google Google Cloud Spanner JDBC Snippets</name>
+  <url>https://github.com/googleapis/java-spanner-jdbc</url>
+
+  <!--
+    The parent pom defines common style checks and testing strategies for our samples.
+    Removing or replacing it should not affect the execution of the samples in anyway.
+  -->
+  <parent>
+    <groupId>com.google.cloud.samples</groupId>
+    <artifactId>shared-configuration</artifactId>
+    <version>1.0.12</version>
+  </parent>
+
+  <properties>
+    <maven.compiler.target>1.8</maven.compiler.target>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+
+  <!-- [START spanner-jdbc_install_with_bom] -->
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>com.google.cloud</groupId>
+        <artifactId>libraries-bom</artifactId>
+        <version></version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.cloud</groupId>
+      <artifactId>google-cloud-spanner-jdbc</artifactId>
+    </dependency>
+    <!-- [END spanner-jdbc_install_with_bom] -->
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.13</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.truth</groupId>
+      <artifactId>truth</artifactId>
+      <version>1.0.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
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.metadata b/synth.metadata
index 699d0d7..ee2cf17 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -1,5 +1,5 @@
 {
-  "updateTime": "2020-03-14T09:16:59.709595Z",
+  "updateTime": "2020-03-17T19:21:53.454785Z",
   "sources": [
     {
       "template": {
diff --git a/versions.txt b/versions.txt
index 72f0ebd..ce565c7 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
 # Format:
 # module:released-version:current-version
 
-google-cloud-spanner-jdbc:1.13.0:1.13.1-SNAPSHOT
+google-cloud-spanner-jdbc:1.14.0:1.14.1-SNAPSHOT


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';