diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml
index a48787484..7e8b94667 100644
--- a/.github/workflows/unmanaged_dependency_check.yaml
+++ b/.github/workflows/unmanaged_dependency_check.yaml
@@ -14,6 +14,6 @@ jobs:
shell: bash
run: .kokoro/build.sh
- name: Unmanaged dependency check
- uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.43.0
+ uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.44.0
with:
bom-path: pom.xml
diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-17.cfg
index 8d3f11dc4..728751d04 100644
--- a/.kokoro/presubmit/graalvm-native-17.cfg
+++ b/.kokoro/presubmit/graalvm-native-17.cfg
@@ -3,7 +3,7 @@
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.43.0"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.44.0"
}
env_vars: {
diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native.cfg
index f78bafd26..39151469c 100644
--- a/.kokoro/presubmit/graalvm-native.cfg
+++ b/.kokoro/presubmit/graalvm-native.cfg
@@ -3,7 +3,7 @@
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.43.0"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.44.0"
}
env_vars: {
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a20633643..b0b5090c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+## [2.27.1](https://github.com/googleapis/java-spanner-jdbc/compare/v2.27.0...v2.27.1) (2025-02-28)
+
+
+### Bug Fixes
+
+* Include COLUMN_DEFAULT in the returned metadata ([#1937](https://github.com/googleapis/java-spanner-jdbc/issues/1937)) ([98eb542](https://github.com/googleapis/java-spanner-jdbc/commit/98eb542201330188570ef995e38fa2a690e1160f))
+
+
+### Dependencies
+
+* Update dependency com.google.api.grpc:proto-google-cloud-trace-v1 to v2.59.0 ([#1938](https://github.com/googleapis/java-spanner-jdbc/issues/1938)) ([5458023](https://github.com/googleapis/java-spanner-jdbc/commit/54580230f61584f0691730693497171ff9bfc734))
+* Update dependency com.google.cloud:google-cloud-spanner-bom to v6.88.0 ([#1939](https://github.com/googleapis/java-spanner-jdbc/issues/1939)) ([3c23e90](https://github.com/googleapis/java-spanner-jdbc/commit/3c23e90b7a7dee52169db8041523f38a7ceeb6ad))
+* Update dependency com.google.cloud:google-cloud-trace to v2.59.0 ([#1940](https://github.com/googleapis/java-spanner-jdbc/issues/1940)) ([b31dd6e](https://github.com/googleapis/java-spanner-jdbc/commit/b31dd6e78b5f9b529b7cc7a9a34ea176231e22dc))
+* Update dependency com.google.cloud:sdk-platform-java-config to v3.44.0 ([#1931](https://github.com/googleapis/java-spanner-jdbc/issues/1931)) ([568a464](https://github.com/googleapis/java-spanner-jdbc/commit/568a464f29055383b7930deb42505bedad506339))
+
+
+### Documentation
+
+* Add defaultSequenceKind connection property documentation ([#1935](https://github.com/googleapis/java-spanner-jdbc/issues/1935)) ([c30b09a](https://github.com/googleapis/java-spanner-jdbc/commit/c30b09ab554d57adccaee72c36969407bbb1d4dd))
+
## [2.27.0](https://github.com/googleapis/java-spanner-jdbc/compare/v2.26.1...v2.27.0) (2025-02-21)
diff --git a/README.md b/README.md
index 85576faed..a2fda612e 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ If you are using Maven, add this to your pom.xml file:
com.google.cloud
google-cloud-spanner-jdbc
- 2.27.0
+ 2.27.1
```
@@ -30,7 +30,7 @@ If you are using Gradle without BOM, add this to your dependencies
```Groovy
-implementation 'com.google.cloud:google-cloud-spanner-jdbc:2.27.0'
+implementation 'com.google.cloud:google-cloud-spanner-jdbc:2.27.1'
```
@@ -38,7 +38,7 @@ If you are using SBT, add this to your dependencies
```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "2.27.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "2.27.1"
```
diff --git a/documentation/connection_properties.md b/documentation/connection_properties.md
index 52938f4ef..887278da1 100644
--- a/documentation/connection_properties.md
+++ b/documentation/connection_properties.md
@@ -22,6 +22,7 @@ The 'Context' value indicates whether the property can only be set when a connec
| databaserole | Sets the database role to use for this connection. The default is privileges assigned to IAM role | | | STARTUP |
| databoostenabled | Enable data boost for all partitioned queries that are executed by this connection. This setting is only used for partitioned queries and is ignored by all other statements. | false | true, false | USER |
| ddlintransactionmode | Determines how the connection should handle DDL statements in a read/write transaction. | ALLOW_IN_EMPTY_TRANSACTION | FAIL, ALLOW_IN_EMPTY_TRANSACTION, AUTO_COMMIT_TRANSACTION | USER |
+| defaultsequencekind | The default sequence kind that should be used for the database. This property is only used when a DDL statement that requires a default sequence kind is executed on this connection. | | | USER |
| delaytransactionstartuntilfirstwrite | Enabling this option will delay the actual start of a read/write transaction until the first write operation is seen in that transaction. All reads that happen before the first write in a transaction will instead be executed as if the connection was in auto-commit mode. Enabling this option will make read/write transactions lose their SERIALIZABLE isolation level. Read operations that are executed after the first write operation in a read/write transaction will be executed using the read/write transaction. Enabling this mode can reduce locking and improve performance for applications that can handle the lower transaction isolation semantics. | false | true, false | USER |
| dialect | Sets the dialect to use for new databases that are created by this connection. | GOOGLE_STANDARD_SQL | GOOGLE_STANDARD_SQL, POSTGRESQL | STARTUP |
| directed_read | The directed read options to apply to read-only transactions. | | | USER |
diff --git a/pom.xml b/pom.xml
index 972790d25..a201ba9ba 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">
4.0.0
google-cloud-spanner-jdbc
- 2.27.0
+ 2.27.1
jar
Google Cloud Spanner JDBC
https://github.com/googleapis/java-spanner-jdbc
@@ -14,7 +14,7 @@
com.google.cloud
sdk-platform-java-config
- 3.43.0
+ 3.44.0
@@ -61,7 +61,7 @@
com.google.cloud
google-cloud-spanner-bom
- 6.87.0
+ 6.88.0
pom
import
@@ -216,13 +216,13 @@
com.google.cloud
google-cloud-trace
- 2.58.0
+ 2.59.0
test
com.google.api.grpc
proto-google-cloud-trace-v1
- 2.58.0
+ 2.59.0
test
@@ -468,7 +468,7 @@
org.apache.maven.plugins
maven-project-info-reports-plugin
- 3.8.0
+ 3.9.0
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index ef9e179f9..9456eaec5 100644
--- a/samples/install-without-bom/pom.xml
+++ b/samples/install-without-bom/pom.xml
@@ -29,7 +29,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.26.1
+ 2.27.0
diff --git a/samples/pom.xml b/samples/pom.xml
index bfe9954b2..c36572ade 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -39,7 +39,7 @@
org.apache.maven.plugins
maven-deploy-plugin
- 3.1.3
+ 3.1.4
true
diff --git a/samples/quickperf/pom.xml b/samples/quickperf/pom.xml
index 31a5cb50c..20eabefcf 100644
--- a/samples/quickperf/pom.xml
+++ b/samples/quickperf/pom.xml
@@ -12,7 +12,7 @@
com.google.cloud
sdk-platform-java-config
- 3.43.0
+ 3.44.0
@@ -27,7 +27,7 @@
com.google.cloud
libraries-bom
- 26.54.0
+ 26.55.0
pom
import
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index e6a944729..821d6dd7f 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -29,7 +29,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.27.0
+ 2.27.1
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 6e5560f6e..2135dd40e 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -9,7 +9,7 @@
com.google.cloud
sdk-platform-java-config
- 3.43.0
+ 3.44.0
@@ -26,7 +26,7 @@
com.google.cloud
libraries-bom
- 26.54.0
+ 26.55.0
pom
import
diff --git a/samples/spring-data-jdbc/pom.xml b/samples/spring-data-jdbc/pom.xml
index a9a18f70e..2242e3d78 100644
--- a/samples/spring-data-jdbc/pom.xml
+++ b/samples/spring-data-jdbc/pom.xml
@@ -30,14 +30,14 @@
com.google.cloud
google-cloud-spanner-bom
- 6.87.0
+ 6.88.0
import
pom
com.google.cloud
libraries-bom
- 26.54.0
+ 26.55.0
import
pom
diff --git a/samples/spring-data-mybatis/pom.xml b/samples/spring-data-mybatis/pom.xml
index 1e6f6ac62..f556e1f68 100644
--- a/samples/spring-data-mybatis/pom.xml
+++ b/samples/spring-data-mybatis/pom.xml
@@ -35,7 +35,7 @@
com.google.cloud
libraries-bom
- 26.54.0
+ 26.55.0
import
pom
diff --git a/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetColumns.sql b/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetColumns.sql
index 94d2a0e61..b59f3b78a 100644
--- a/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetColumns.sql
+++ b/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetColumns.sql
@@ -63,7 +63,7 @@ SELECT TABLE_CATALOG AS TABLE_CAT, TABLE_SCHEMA AS TABLE_SCHEM, TABLE_NAME, COLU
ELSE 2
END AS NULLABLE,
NULL AS REMARKS,
- NULL AS COLUMN_DEF,
+ COLUMN_DEFAULT AS COLUMN_DEF,
0 AS SQL_DATA_TYPE,
0 AS SQL_DATETIME_SUB,
CASE
diff --git a/src/main/resources/com/google/cloud/spanner/jdbc/postgresql/DatabaseMetaData_GetColumns.sql b/src/main/resources/com/google/cloud/spanner/jdbc/postgresql/DatabaseMetaData_GetColumns.sql
index 38c105108..5924f980f 100644
--- a/src/main/resources/com/google/cloud/spanner/jdbc/postgresql/DatabaseMetaData_GetColumns.sql
+++ b/src/main/resources/com/google/cloud/spanner/jdbc/postgresql/DatabaseMetaData_GetColumns.sql
@@ -62,7 +62,7 @@ SELECT TABLE_CATALOG AS "TABLE_CAT", TABLE_SCHEMA AS "TABLE_SCHEM", TABLE_NAME A
ELSE 2
END AS "NULLABLE",
NULL AS "REMARKS",
- NULL AS "COLUMN_DEF",
+ COLUMN_DEFAULT AS "COLUMN_DEF",
0 AS "SQL_DATA_TYPE",
0 AS "SQL_DATETIME_SUB",
CHARACTER_MAXIMUM_LENGTH AS "CHAR_OCTET_LENGTH",
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/MultiplexedSessionsTest.java b/src/test/java/com/google/cloud/spanner/jdbc/MultiplexedSessionsTest.java
new file mode 100644
index 000000000..b7b2d2869
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/MultiplexedSessionsTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2025 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.google.cloud.spanner.MockServerHelper;
+import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
+import com.google.cloud.spanner.connection.AbstractMockServerTest;
+import com.google.common.base.Strings;
+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.ResultSetStats;
+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 java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collections;
+import java.util.Map;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MultiplexedSessionsTest extends AbstractMockServerTest {
+ private static final String PROCESS_ENVIRONMENT = "java.lang.ProcessEnvironment";
+ private static final String ENVIRONMENT = "theUnmodifiableEnvironment";
+ private static final String SOURCE_MAP = "m";
+ private static final Object STATIC_METHOD = null;
+ private static final Class> UMODIFIABLE_MAP_CLASS =
+ Collections.unmodifiableMap(Collections.emptyMap()).getClass();
+ private static final Class> MAP_CLASS = Map.class;
+
+ private static boolean setEnvVar = false;
+
+ private String query;
+ private String dml;
+ private String dmlReturning;
+
+ @SuppressWarnings("unchecked")
+ private static Map getModifiableEnvironment() throws Exception {
+ Class> environmentClass = Class.forName(PROCESS_ENVIRONMENT);
+ java.lang.reflect.Field environmentField = environmentClass.getDeclaredField(ENVIRONMENT);
+ assertNotNull(environmentField);
+ environmentField.setAccessible(true);
+
+ Object unmodifiableEnvironmentMap = environmentField.get(STATIC_METHOD);
+ assertNotNull(unmodifiableEnvironmentMap);
+ assertTrue(UMODIFIABLE_MAP_CLASS.isAssignableFrom(unmodifiableEnvironmentMap.getClass()));
+
+ java.lang.reflect.Field underlyingMapField =
+ unmodifiableEnvironmentMap.getClass().getDeclaredField(SOURCE_MAP);
+ underlyingMapField.setAccessible(true);
+ Object underlyingMap = underlyingMapField.get(unmodifiableEnvironmentMap);
+ assertNotNull(underlyingMap);
+ assertTrue(MAP_CLASS.isAssignableFrom(underlyingMap.getClass()));
+
+ return (Map) underlyingMap;
+ }
+
+ @BeforeClass
+ public static void setEnvVars() throws Exception {
+ // Java versions 8 and lower start with 1. (1.8, 1.7 etc.).
+ // Higher versions start with the major version number.
+ // So this effectively verifies that the test is running on Java 8.
+ assumeTrue(System.getProperty("java.version", "undefined").startsWith("1."));
+ assumeFalse(System.getProperty("os.name", "").toLowerCase().startsWith("windows"));
+
+ if (Strings.isNullOrEmpty(System.getenv("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW"))) {
+ Map env = getModifiableEnvironment();
+ env.put("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW", "true");
+ setEnvVar = true;
+ }
+ }
+
+ @AfterClass
+ public static void clearEnvVars() throws Exception {
+ if (setEnvVar) {
+ Map env = getModifiableEnvironment();
+ env.remove("GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW");
+ }
+ }
+
+ @Before
+ public void setupResults() {
+ query = "select * from my_table";
+ dml = "insert into my_table (id, value) values (1, 'One')";
+ String DML_THEN_RETURN_ID = dml + "\nTHEN RETURN `id`";
+ dmlReturning = "insert into my_table (id, value) values (1, 'One') THEN RETURN *";
+
+ super.setupResults();
+
+ com.google.spanner.v1.ResultSet resultSet =
+ com.google.spanner.v1.ResultSet.newBuilder()
+ .setMetadata(
+ ResultSetMetadata.newBuilder()
+ .setRowType(
+ StructType.newBuilder()
+ .addFields(
+ Field.newBuilder()
+ .setType(Type.newBuilder().setCode(TypeCode.INT64).build())
+ .setName("id")
+ .build())
+ .addFields(
+ Field.newBuilder()
+ .setType(Type.newBuilder().setCode(TypeCode.STRING).build())
+ .setName("value")
+ .build())
+ .build())
+ .build())
+ .addRows(
+ ListValue.newBuilder()
+ .addValues(Value.newBuilder().setStringValue("1").build())
+ .addValues(Value.newBuilder().setStringValue("One").build())
+ .build())
+ .build();
+ com.google.spanner.v1.ResultSet returnIdResultSet =
+ com.google.spanner.v1.ResultSet.newBuilder()
+ .setMetadata(
+ ResultSetMetadata.newBuilder()
+ .setRowType(
+ StructType.newBuilder()
+ .addFields(
+ Field.newBuilder()
+ .setType(Type.newBuilder().setCode(TypeCode.INT64).build())
+ .setName("id")
+ .build())
+ .build())
+ .build())
+ .addRows(
+ ListValue.newBuilder()
+ .addValues(Value.newBuilder().setStringValue("1").build())
+ .build())
+ .build();
+ mockSpanner.putStatementResult(
+ StatementResult.query(com.google.cloud.spanner.Statement.of(query), resultSet));
+ mockSpanner.putStatementResult(
+ StatementResult.update(com.google.cloud.spanner.Statement.of(dml), 1L));
+ mockSpanner.putStatementResult(
+ StatementResult.query(
+ com.google.cloud.spanner.Statement.of(dmlReturning),
+ resultSet
+ .toBuilder()
+ .setStats(ResultSetStats.newBuilder().setRowCountExact(1L).build())
+ .build()));
+ mockSpanner.putStatementResult(
+ StatementResult.query(
+ com.google.cloud.spanner.Statement.of(DML_THEN_RETURN_ID),
+ returnIdResultSet
+ .toBuilder()
+ .setStats(ResultSetStats.newBuilder().setRowCountExact(1L).build())
+ .build()));
+ }
+
+ private String createUrl() {
+ return String.format(
+ "jdbc:cloudspanner://localhost:%d/projects/%s/instances/%s/databases/%s?usePlainText=true",
+ getPort(), "proj", "inst", "db");
+ }
+
+ @Override
+ protected Connection createJdbcConnection() throws SQLException {
+ return DriverManager.getConnection(createUrl());
+ }
+
+ @Test
+ public void testStatementExecuteQuery() throws SQLException {
+ try (Connection connection = createJdbcConnection();
+ Statement statement = connection.createStatement()) {
+ try (ResultSet resultSet = statement.executeQuery(query)) {
+ //noinspection StatementWithEmptyBody
+ while (resultSet.next()) {}
+ }
+ }
+ assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
+ ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
+ assertTrue(MockServerHelper.getSession(mockSpanner, request.getSession()).getMultiplexed());
+ }
+
+ @Test
+ public void testStatementExecuteUpdate() throws SQLException {
+ try (Connection connection = createJdbcConnection();
+ Statement statement = connection.createStatement()) {
+ assertEquals(1, statement.executeUpdate(dml));
+ }
+ assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
+ ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
+ assertTrue(MockServerHelper.getSession(mockSpanner, request.getSession()).getMultiplexed());
+ assertTrue(request.hasTransaction());
+ assertTrue(request.getTransaction().hasBegin());
+ assertTrue(request.getTransaction().getBegin().hasReadWrite());
+ }
+
+ @Test
+ public void testStatementExecuteQueryDmlReturning() throws SQLException {
+ try (Connection connection = createJdbcConnection();
+ Statement statement = connection.createStatement()) {
+ try (ResultSet resultSet = statement.executeQuery(dmlReturning)) {
+ //noinspection StatementWithEmptyBody
+ while (resultSet.next()) {}
+ }
+ }
+ assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
+ ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
+ assertTrue(MockServerHelper.getSession(mockSpanner, request.getSession()).getMultiplexed());
+ assertTrue(request.hasTransaction());
+ assertTrue(request.getTransaction().hasBegin());
+ assertTrue(request.getTransaction().getBegin().hasReadWrite());
+ }
+}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcDatabaseMetaDataTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcDatabaseMetaDataTest.java
index 94d9ad140..0ebd0031a 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcDatabaseMetaDataTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcDatabaseMetaDataTest.java
@@ -21,7 +21,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
@@ -62,10 +61,6 @@ public class ITJdbcDatabaseMetaDataTest extends ITAbstractJdbcTest {
@BeforeClass
public static void setup() throws Exception {
- assumeFalse(
- "Named schemas are not yet supported on the emulator",
- EmulatorSpannerHelper.isUsingEmulator());
-
database =
env.getOrCreateDatabase(
Dialect.GOOGLE_STANDARD_SQL, getMusicTablesDdl(Dialect.GOOGLE_STANDARD_SQL));
@@ -89,6 +84,18 @@ public static void setup() throws Exception {
.map(statement -> statement.replace(" ON ", " ON test."))
.map(statement -> statement.replace(" ON test.DELETE", " ON DELETE"))
.map(statement -> statement.replace(" REFERENCES ", " REFERENCES test."))
+ .map(
+ statement ->
+ EmulatorSpannerHelper.isUsingEmulator()
+ ? statement.replace("Fk_Concerts_Singer", "test_Fk_Concerts_Singer")
+ : statement)
+ .map(
+ statement ->
+ EmulatorSpannerHelper.isUsingEmulator()
+ ? statement.replace(
+ "Fk_TableWithRef_TableWithAllColumnTypes",
+ "test_Fk_TableWithRef_TableWithAllColumnTypes")
+ : statement)
.collect(Collectors.toList());
tables.add(0, "create schema test");
client
@@ -110,6 +117,7 @@ private static final class Column {
private final boolean nullable;
private final Integer charOctetLength;
private final boolean computed;
+ private final String defaultValue;
private Column(
String name,
@@ -120,7 +128,17 @@ private Column(
Integer radix,
boolean nullable,
Integer charOctetLength) {
- this(name, type, typeName, colSize, decimalDigits, radix, nullable, charOctetLength, false);
+ this(
+ name,
+ type,
+ typeName,
+ colSize,
+ decimalDigits,
+ radix,
+ nullable,
+ charOctetLength,
+ false,
+ null);
}
private Column(
@@ -132,7 +150,8 @@ private Column(
Integer radix,
boolean nullable,
Integer charOctetLength,
- boolean computed) {
+ boolean computed,
+ String defaultValue) {
this.name = name;
this.type = type;
this.typeName = typeName;
@@ -142,21 +161,42 @@ private Column(
this.nullable = nullable;
this.charOctetLength = charOctetLength;
this.computed = computed;
+ this.defaultValue = defaultValue;
}
}
private static final List EXPECTED_COLUMNS =
Arrays.asList(
new Column("ColInt64", Types.BIGINT, "INT64", 19, null, 10, false, null),
- new Column("ColFloat64", Types.DOUBLE, "FLOAT64", 15, 16, 2, false, null),
- new Column("ColFloat32", Types.REAL, "FLOAT32", 15, 16, 2, false, null),
+ new Column("ColFloat64", Types.DOUBLE, "FLOAT64", 15, 16, 2, false, null, false, "0.0"),
+ new Column("ColFloat32", Types.REAL, "FLOAT32", 15, 16, 2, false, null, false, "0.0"),
new Column("ColBool", Types.BOOLEAN, "BOOL", null, null, null, false, null),
- new Column("ColString", Types.NVARCHAR, "STRING(100)", 100, null, null, false, 100),
+ new Column(
+ "ColString",
+ Types.NVARCHAR,
+ "STRING(100)",
+ 100,
+ null,
+ null,
+ false,
+ 100,
+ false,
+ "'Hello World!'"),
new Column(
"ColStringMax", Types.NVARCHAR, "STRING(MAX)", 2621440, null, null, false, 2621440),
new Column("ColBytes", Types.BINARY, "BYTES(100)", 100, null, null, false, null),
new Column("ColBytesMax", Types.BINARY, "BYTES(MAX)", 10485760, null, null, false, null),
- new Column("ColDate", Types.DATE, "DATE", 10, null, null, false, null),
+ new Column(
+ "ColDate",
+ Types.DATE,
+ "DATE",
+ 10,
+ null,
+ null,
+ false,
+ null,
+ false,
+ "DATE '2000-01-01'"),
new Column("ColTimestamp", Types.TIMESTAMP, "TIMESTAMP", 35, null, null, false, null),
new Column("ColCommitTS", Types.TIMESTAMP, "TIMESTAMP", 35, null, null, false, null),
new Column("ColNumeric", Types.NUMERIC, "NUMERIC", 15, null, 10, false, null),
@@ -202,7 +242,8 @@ private Column(
null,
true,
2621440,
- true));
+ true,
+ null));
@Test
public void testGetColumns() throws SQLException {
@@ -244,7 +285,7 @@ public void testGetColumns() throws SQLException {
col.nullable ? DatabaseMetaData.columnNullable : DatabaseMetaData.columnNoNulls,
rs.getInt("NULLABLE"));
assertNull(rs.getString("REMARKS"));
- assertNull(rs.getString("COLUMN_DEF"));
+ assertEquals(col.defaultValue, rs.getString("COLUMN_DEF"));
assertEquals(0, rs.getInt("SQL_DATA_TYPE"));
assertEquals(0, rs.getInt("SQL_DATETIME_SUB"));
if (col.charOctetLength == null) {
@@ -360,7 +401,11 @@ public void testGetCrossReferences() throws SQLException {
assertEquals(1, rs.getShort("KEY_SEQ"));
assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertEquals("Fk_Concerts_Singer", rs.getString("FK_NAME"));
+ if (EmulatorSpannerHelper.isUsingEmulator() && "test".equals(schema)) {
+ assertEquals("test_Fk_Concerts_Singer", rs.getString("FK_NAME"));
+ } else {
+ assertEquals("Fk_Concerts_Singer", rs.getString("FK_NAME"));
+ }
assertEquals("PK_Singers", rs.getString("PK_NAME"));
assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
assertFalse(rs.next());
@@ -389,7 +434,11 @@ public void testGetCrossReferences() throws SQLException {
assertEquals(1, rs.getShort("KEY_SEQ"));
assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ if (EmulatorSpannerHelper.isUsingEmulator() && "test".equals(schema)) {
+ assertEquals("test_Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ } else {
+ assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ }
assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
assertTrue(rs.next());
@@ -404,7 +453,11 @@ public void testGetCrossReferences() throws SQLException {
assertEquals(2, rs.getShort("KEY_SEQ"));
assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ if (EmulatorSpannerHelper.isUsingEmulator() && "test".equals(schema)) {
+ assertEquals("test_Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ } else {
+ assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ }
assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
assertTrue(rs.next());
@@ -419,7 +472,11 @@ public void testGetCrossReferences() throws SQLException {
assertEquals(3, rs.getShort("KEY_SEQ"));
assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ if (EmulatorSpannerHelper.isUsingEmulator() && "test".equals(schema)) {
+ assertEquals("test_Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ } else {
+ assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ }
assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
assertFalse(rs.next());
@@ -519,6 +576,12 @@ public void testGetIndexInfo() throws SQLException {
connection.getMetaData().getIndexInfo(DEFAULT_CATALOG, schema, null, false, false)) {
for (IndexInfo index : EXPECTED_INDICES) {
+ // The emulator does not generate indexes for foreign keys in a non-default schema.
+ if (EmulatorSpannerHelper.isUsingEmulator()
+ && "test".equals(schema)
+ && ("FOREIGN_KEY".equals(index.indexName) || "GENERATED".equals(index.indexName))) {
+ continue;
+ }
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
assertEquals(schema, rs.getString("TABLE_SCHEM"));
@@ -612,7 +675,11 @@ private void assertImportedKeysTableWithRef(String schema, ResultSet rs) throws
assertEquals(1, rs.getShort("KEY_SEQ"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("UPDATE_RULE"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("DELETE_RULE"));
- assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ if (EmulatorSpannerHelper.isUsingEmulator() && "test".equals(schema)) {
+ assertEquals("test_Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ } else {
+ assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ }
assertNotNull(rs.getString("PK_NAME")); // Index name is generated.
assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getInt("DEFERRABILITY"));
@@ -628,7 +695,11 @@ private void assertImportedKeysTableWithRef(String schema, ResultSet rs) throws
assertEquals(2, rs.getShort("KEY_SEQ"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("UPDATE_RULE"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("DELETE_RULE"));
- assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ if (EmulatorSpannerHelper.isUsingEmulator() && "test".equals(schema)) {
+ assertEquals("test_Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ } else {
+ assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ }
assertNotNull(rs.getString("PK_NAME")); // Index name is generated.
assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getInt("DEFERRABILITY"));
@@ -644,7 +715,11 @@ private void assertImportedKeysTableWithRef(String schema, ResultSet rs) throws
assertEquals(3, rs.getShort("KEY_SEQ"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("UPDATE_RULE"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("DELETE_RULE"));
- assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ if (EmulatorSpannerHelper.isUsingEmulator() && "test".equals(schema)) {
+ assertEquals("test_Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ } else {
+ assertEquals("Fk_TableWithRef_TableWithAllColumnTypes", rs.getString("FK_NAME"));
+ }
assertNotNull(rs.getString("PK_NAME")); // Index name is generated.
assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getInt("DEFERRABILITY"));
@@ -684,7 +759,11 @@ private void assertImportedKeysConcerts(String schema, ResultSet rs) throws SQLE
assertEquals(1, rs.getShort("KEY_SEQ"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("UPDATE_RULE"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("DELETE_RULE"));
- assertEquals("Fk_Concerts_Singer", rs.getString("FK_NAME"));
+ if (EmulatorSpannerHelper.isUsingEmulator() && "test".equals(schema)) {
+ assertEquals("test_Fk_Concerts_Singer", rs.getString("FK_NAME"));
+ } else {
+ assertEquals("Fk_Concerts_Singer", rs.getString("FK_NAME"));
+ }
assertEquals("PK_Singers", rs.getString("PK_NAME"));
assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getInt("DEFERRABILITY"));
@@ -720,7 +799,11 @@ private void assertExportedKeysSingers(String schema, ResultSet rs) throws SQLEx
assertEquals(1, rs.getShort("KEY_SEQ"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("UPDATE_RULE"));
assertEquals(DatabaseMetaData.importedKeyRestrict, rs.getInt("DELETE_RULE"));
- assertEquals("Fk_Concerts_Singer", rs.getString("FK_NAME"));
+ if (EmulatorSpannerHelper.isUsingEmulator() && "test".equals(schema)) {
+ assertEquals("test_Fk_Concerts_Singer", rs.getString("FK_NAME"));
+ } else {
+ assertEquals("Fk_Concerts_Singer", rs.getString("FK_NAME"));
+ }
assertEquals("PK_Singers", rs.getString("PK_NAME"));
assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getInt("DEFERRABILITY"));
@@ -888,9 +971,6 @@ public void testGetTables() throws SQLException {
try (ResultSet rs =
connection.getMetaData().getTables(DEFAULT_CATALOG, schema, null, null)) {
for (Table table : EXPECTED_TABLES) {
- if (EmulatorSpannerHelper.isUsingEmulator() && table.name.equals("SingersView")) {
- continue;
- }
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
assertEquals(schema, rs.getString("TABLE_SCHEM"));
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcPreparedStatementTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcPreparedStatementTest.java
index 31a1a7f17..a26137b21 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcPreparedStatementTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcPreparedStatementTest.java
@@ -743,9 +743,21 @@ public void test04_Timestamps() throws SQLException {
Timestamp inDefaultTZ = rs.getTimestamp(4);
assertEquals(testTimestamp.getTime(), inDefaultTZ.getTime());
// Then get it in the test timezone.
- if (testCalendar != null) {
+ if (testCalendar != null
+ && !System.getProperty("java.vm.name", "").toLowerCase().contains("graalvm")
+ && !System.getProperty("java.vendor", "").toLowerCase().contains("graalvm")) {
Timestamp inOtherTZ = rs.getTimestamp(4, testCalendar);
assertEquals(
+ "Timezone: "
+ + testCalendar
+ + ", rawOffset="
+ + testCalendar.getTimeZone().getRawOffset()
+ + ", os="
+ + System.getProperty("os.name")
+ + ", vm="
+ + System.getProperty("java.vm.name")
+ + ", vendor="
+ + System.getProperty("java.vendor"),
testTimestamp.getTime() + testCalendar.getTimeZone().getRawOffset(),
inOtherTZ.getTime());
}
@@ -755,8 +767,19 @@ public void test04_Timestamps() throws SQLException {
inDefaultTZ = rs.getTimestamp(5);
if (testCalendar == null) {
assertEquals(testTimestamp.getTime(), inDefaultTZ.getTime());
- } else {
+ } else if (!System.getProperty("java.vm.name", "").toLowerCase().contains("graalvm")
+ && !System.getProperty("java.vendor", "").toLowerCase().contains("graalvm")) {
assertEquals(
+ "Timezone: "
+ + testCalendar
+ + ", rawOffset="
+ + testCalendar.getTimeZone().getRawOffset()
+ + ", os="
+ + System.getProperty("os.name")
+ + ", vm="
+ + System.getProperty("java.vm.name")
+ + ", vendor="
+ + System.getProperty("java.vendor"),
testTimestamp.getTime() - testCalendar.getTimeZone().getRawOffset(),
inDefaultTZ.getTime());
}
diff --git a/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables.sql b/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables.sql
index 51f7d1ea1..8e4a60b24 100644
--- a/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables.sql
+++ b/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables.sql
@@ -49,7 +49,7 @@ CREATE TABLE Songs (
TrackId INT64 NOT NULL,
SongName STRING(MAX),
Duration INT64,
- SongGenre STRING(25)
+ SongGenre STRING(25) DEFAULT ('Jazz')
) PRIMARY KEY(SingerId, AlbumId, TrackId),
INTERLEAVE IN PARENT Albums ON DELETE CASCADE;
@@ -69,14 +69,14 @@ CREATE TABLE Concerts (
CREATE TABLE TableWithAllColumnTypes (
ColInt64 INT64 NOT NULL,
- ColFloat64 FLOAT64 NOT NULL,
- ColFloat32 FLOAT32 NOT NULL,
+ ColFloat64 FLOAT64 NOT NULL DEFAULT (0.0),
+ ColFloat32 FLOAT32 NOT NULL DEFAULT (0.0),
ColBool BOOL NOT NULL,
- ColString STRING(100) NOT NULL,
+ ColString STRING(100) NOT NULL DEFAULT ('Hello World!'),
ColStringMax STRING(MAX) NOT NULL,
ColBytes BYTES(100) NOT NULL,
ColBytesMax BYTES(MAX) NOT NULL,
- ColDate DATE NOT NULL,
+ ColDate DATE NOT NULL DEFAULT (DATE '2000-01-01'),
ColTimestamp TIMESTAMP NOT NULL,
ColCommitTS TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
ColNumeric NUMERIC NOT NULL,
diff --git a/versions.txt b/versions.txt
index fcd51f942..4623cc481 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-spanner-jdbc:2.27.0:2.27.0
+google-cloud-spanner-jdbc:2.27.1:2.27.1