Repository: GoogleCloudPlatform/pgadapter
Pull request #495 (2022-11-18 19:58:50)

feat: support 'select version()' and similar
Add support for `select version()` and similar commands.

codecov[bot]: # [Codecov](https://codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/495?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) Report
> Merging [#495](https://codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/495?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) (f1e3195) into [postgresql-dialect](https://codecov.io/gh/GoogleCloudPlatform/pgadapter/commit/e6ffe032d30b762da974cc75fda7e7d3f144b4c6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) (e6ffe03) will **increase** coverage by `0.02%`.
> The diff coverage is `100.00%`.

```diff
@@                   Coverage Diff                    @@
##             postgresql-dialect     #495      +/-   ##
========================================================
+ Coverage                 87.51%   87.54%   +0.02%     
- Complexity                 2029     2032       +3     
========================================================
  Files                       122      123       +1     
  Lines                      6666     6680      +14     
  Branches                    960      960              
========================================================
+ Hits                       5834     5848      +14     
  Misses                      583      583              
  Partials                    249      249              
```

| Flag | Coverage Δ | |
|---|---|---|
| all_tests | `87.54% <100.00%> (+0.02%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/495?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) | Coverage Δ | |
|---|---|---|
| [...ud/spanner/pgadapter/utils/ClientAutoDetector.java](https://codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/495/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvcGdhZGFwdGVyL3V0aWxzL0NsaWVudEF1dG9EZXRlY3Rvci5qYXZh) | `93.18% <ø> (ø)` | |
| [...apter/statements/local/SelectVersionStatement.java](https://codecov.io/gh/GoogleCloudPlatform/pgadapter/pull/495/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform#diff-c3JjL21haW4vamF2YS9jb20vZ29vZ2xlL2Nsb3VkL3NwYW5uZXIvcGdhZGFwdGVyL3N0YXRlbWVudHMvbG9jYWwvU2VsZWN0VmVyc2lvblN0YXRlbWVudC5qYXZh) | `100.00% <100.00%> (ø)` | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform)


Patch:
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/local/SelectVersionStatement.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/local/SelectVersionStatement.java
new file mode 100644
index 00000000..59980448
--- /dev/null
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/local/SelectVersionStatement.java
@@ -0,0 +1,81 @@
+// Copyright 2022 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.pgadapter.statements.local;
+
+import com.google.api.core.InternalApi;
+import com.google.cloud.spanner.ResultSet;
+import com.google.cloud.spanner.ResultSets;
+import com.google.cloud.spanner.Struct;
+import com.google.cloud.spanner.Type;
+import com.google.cloud.spanner.Type.StructField;
+import com.google.cloud.spanner.connection.StatementResult;
+import com.google.cloud.spanner.pgadapter.statements.BackendConnection;
+import com.google.cloud.spanner.pgadapter.statements.BackendConnection.QueryResult;
+import com.google.common.collect.ImmutableList;
+
+@InternalApi
+public class SelectVersionStatement implements LocalStatement {
+  public static final SelectVersionStatement INSTANCE = new SelectVersionStatement();
+
+  private SelectVersionStatement() {}
+
+  @Override
+  public String[] getSql() {
+    return new String[] {
+      "select version()",
+      "SELECT version()",
+      "Select version()",
+      "select VERSION()",
+      "SELECT VERSION()",
+      "Select VERSION()",
+      "select * from version()",
+      "SELECT * FROM version()",
+      "Select * from version()",
+      "select * from VERSION()",
+      "SELECT * FROM VERSION()",
+      "Select * from VERSION()",
+      "select pg_catalog.version()",
+      "SELECT pg_catalog.version()",
+      "Select pg_catalog.version()",
+      "select PG_CATALOG.VERSION()",
+      "SELECT PG_CATALOG.VERSION()",
+      "Select PG_CATALOG.VERSION()",
+      "select * from pg_catalog.version()",
+      "SELECT * FROM pg_catalog.version()",
+      "Select * from pg_catalog.version()",
+      "select * from PG_CATALOG.VERSION()",
+      "SELECT * FROM PG_CATALOG.VERSION()",
+      "Select * from PG_CATALOG.VERSION()"
+    };
+  }
+
+  @Override
+  public StatementResult execute(BackendConnection backendConnection) {
+    ResultSet resultSet =
+        ResultSets.forRows(
+            Type.struct(StructField.of("version", Type.string())),
+            ImmutableList.of(
+                Struct.newBuilder()
+                    .set("version")
+                    .to(
+                        "PostgreSQL "
+                            + backendConnection
+                                .getSessionState()
+                                .get(null, "server_version")
+                                .getSetting())
+                    .build()));
+    return new QueryResult(resultSet);
+  }
+}
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/utils/ClientAutoDetector.java b/src/main/java/com/google/cloud/spanner/pgadapter/utils/ClientAutoDetector.java
index e53fa52d..4d5ecec2 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/utils/ClientAutoDetector.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/utils/ClientAutoDetector.java
@@ -22,6 +22,7 @@ import com.google.cloud.spanner.pgadapter.statements.local.LocalStatement;
 import com.google.cloud.spanner.pgadapter.statements.local.SelectCurrentCatalogStatement;
 import com.google.cloud.spanner.pgadapter.statements.local.SelectCurrentDatabaseStatement;
 import com.google.cloud.spanner.pgadapter.statements.local.SelectCurrentSchemaStatement;
+import com.google.cloud.spanner.pgadapter.statements.local.SelectVersionStatement;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import java.util.List;
@@ -41,6 +42,7 @@ public class ClientAutoDetector {
           SelectCurrentSchemaStatement.INSTANCE,
           SelectCurrentDatabaseStatement.INSTANCE,
           SelectCurrentCatalogStatement.INSTANCE,
+          SelectVersionStatement.INSTANCE,
           DjangoGetTableNamesStatement.INSTANCE);
 
   public enum WellKnownClient {


Test patch:
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java
index 7707972b..3add6fa7 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/JdbcMockServerTest.java
@@ -227,6 +227,31 @@ public class JdbcMockServerTest extends AbstractMockServerTest {
     assertEquals(0, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
   }
 
+  @Test
+  public void testSelectVersion() throws SQLException {
+    for (String sql :
+        new String[] {"SELECT version()", "select version()", "select * from version()"}) {
+
+      try (Connection connection = DriverManager.getConnection(createUrl())) {
+        String version = null;
+        try (ResultSet resultSet =
+            connection.createStatement().executeQuery("show server_version")) {
+          assertTrue(resultSet.next());
+          version = resultSet.getString(1);
+          assertFalse(resultSet.next());
+        }
+        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
+          assertTrue(resultSet.next());
+          assertEquals("PostgreSQL " + version, resultSet.getString("version"));
+          assertFalse(resultSet.next());
+        }
+      }
+    }
+
+    // The statement is handled locally and not sent to Cloud Spanner.
+    assertEquals(0, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
+  }
+
   @Test
   public void testShowSearchPath() throws SQLException {
     String sql = "show search_path";
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/statements/local/SelectVersionStatementTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/statements/local/SelectVersionStatementTest.java
new file mode 100644
index 00000000..6f414bc0
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/statements/local/SelectVersionStatementTest.java
@@ -0,0 +1,52 @@
+// Copyright 2022 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.pgadapter.statements.local;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.cloud.spanner.ResultSet;
+import com.google.cloud.spanner.pgadapter.session.PGSetting;
+import com.google.cloud.spanner.pgadapter.session.SessionState;
+import com.google.cloud.spanner.pgadapter.statements.BackendConnection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SelectVersionStatementTest {
+  @Test
+  public void testExecute() {
+    for (String version : new String[] {"14.1", "10.0"}) {
+      BackendConnection backendConnection = mock(BackendConnection.class);
+      SessionState sessionState = mock(SessionState.class);
+      PGSetting pgSetting = mock(PGSetting.class);
+      when(backendConnection.getSessionState()).thenReturn(sessionState);
+      when(sessionState.get(null, "server_version")).thenReturn(pgSetting);
+      when(pgSetting.getSetting()).thenReturn(version);
+
+      try (ResultSet resultSet =
+          SelectVersionStatement.INSTANCE.execute(backendConnection).getResultSet()) {
+        assertTrue(resultSet.next());
+        assertEquals(1, resultSet.getColumnCount());
+        assertEquals("PostgreSQL " + version, resultSet.getString("version"));
+        assertFalse(resultSet.next());
+      }
+    }
+  }
+}