diff --git a/.github/release-please.yml b/.github/release-please.yml
index cbc451149..bd2014774 100644
--- a/.github/release-please.yml
+++ b/.github/release-please.yml
@@ -6,4 +6,4 @@ branches:
releaseType: java-yoshi
bumpMinorPreMajor: true
handleGHRelease: true
-
+extraFiles: ["README.md"]
diff --git a/.github/workflows/quickperf.yaml b/.github/workflows/quickperf.yaml
new file mode 100644
index 000000000..03c242fef
--- /dev/null
+++ b/.github/workflows/quickperf.yaml
@@ -0,0 +1,30 @@
+# Copyright 2024 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.
+# Github action job to test core java library features on
+# downstream client libraries before they are released.
+on:
+ pull_request:
+name: quickperf
+jobs:
+ quickperf:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 17
+ - name: Run tests
+ run: mvn test
+ working-directory: samples/quickperf
diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml
index 09f954387..8d462abb5 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.33.0
+ uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.34.0
with:
bom-path: pom.xml
diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-17.cfg
index 7008a7215..53cd15405 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.33.0"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.34.0"
}
env_vars: {
diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native.cfg
index 931f9bb00..e211e47fc 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.33.0"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.34.0"
}
env_vars: {
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 424389937..e48516502 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,34 @@
# Changelog
+## [2.21.0](https://github.com/googleapis/java-spanner-jdbc/compare/v2.20.2...v2.21.0) (2024-08-23)
+
+
+### Features
+
+* Add Quickperf for simple performance testing with JDBC ([#1619](https://github.com/googleapis/java-spanner-jdbc/issues/1619)) ([b6bbd8f](https://github.com/googleapis/java-spanner-jdbc/commit/b6bbd8f40c1ce61914e2c7b80be04abbf4e346ab))
+
+
+### Dependencies
+
+* Update dependency com.fasterxml.jackson.core:jackson-databind to v2.13.4.2 [security] ([#1710](https://github.com/googleapis/java-spanner-jdbc/issues/1710)) ([eff5df2](https://github.com/googleapis/java-spanner-jdbc/commit/eff5df22785e55a8f0974f028678883ef404b4e6))
+* Update dependency com.fasterxml.jackson.core:jackson-databind to v2.17.2 ([#1715](https://github.com/googleapis/java-spanner-jdbc/issues/1715)) ([21aa199](https://github.com/googleapis/java-spanner-jdbc/commit/21aa19970cee5ee0525c5eaae8bc334cf81d8f25))
+* Update dependency com.google.api.grpc:proto-google-cloud-trace-v1 to v2.48.0 ([#1719](https://github.com/googleapis/java-spanner-jdbc/issues/1719)) ([a40606c](https://github.com/googleapis/java-spanner-jdbc/commit/a40606c2ef75388cfa0733c6955329225f28c71b))
+* Update dependency com.google.cloud:google-cloud-spanner-bom to v6.72.0 ([#1702](https://github.com/googleapis/java-spanner-jdbc/issues/1702)) ([31a961d](https://github.com/googleapis/java-spanner-jdbc/commit/31a961d29c7b51e9dcd5aac8a8a66444abbd9088))
+* Update dependency com.google.cloud:google-cloud-spanner-bom to v6.73.0 ([#1726](https://github.com/googleapis/java-spanner-jdbc/issues/1726)) ([f5f8051](https://github.com/googleapis/java-spanner-jdbc/commit/f5f80517425969f4c1bab4ec1c72afa1ccbb842c))
+* Update dependency com.google.cloud:google-cloud-trace to v2.48.0 ([#1720](https://github.com/googleapis/java-spanner-jdbc/issues/1720)) ([c9b646d](https://github.com/googleapis/java-spanner-jdbc/commit/c9b646d1b9c0ccef9cf8ba3bc58da686fae34bc1))
+* Update dependency com.google.cloud:sdk-platform-java-config to v3.34.0 ([#1705](https://github.com/googleapis/java-spanner-jdbc/issues/1705)) ([f3f0c10](https://github.com/googleapis/java-spanner-jdbc/commit/f3f0c10394e76389dbc4a62e5702fd5f80c57b1a))
+* Update dependency com.spotify.fmt:fmt-maven-plugin to v2.24 ([#1708](https://github.com/googleapis/java-spanner-jdbc/issues/1708)) ([6881512](https://github.com/googleapis/java-spanner-jdbc/commit/68815128ae2c40c224b4ab155b942e8f5313024f))
+* Update dependency commons-cli:commons-cli to v1.9.0 ([#1716](https://github.com/googleapis/java-spanner-jdbc/issues/1716)) ([6f48065](https://github.com/googleapis/java-spanner-jdbc/commit/6f48065952c2fc2716c911a45959326a6bafaa13))
+* Update dependency io.opentelemetry:opentelemetry-bom to v1.41.0 ([#1703](https://github.com/googleapis/java-spanner-jdbc/issues/1703)) ([af58b7a](https://github.com/googleapis/java-spanner-jdbc/commit/af58b7a882edae9a50fbc0d4084cb74b3727d5a6))
+* Update dependency org.apache.commons:commons-lang3 to v3.16.0 ([#1717](https://github.com/googleapis/java-spanner-jdbc/issues/1717)) ([f5229ce](https://github.com/googleapis/java-spanner-jdbc/commit/f5229ce5099b6d2d2b7c099ff4ac1319f21860df))
+* Update dependency org.postgresql:postgresql to v42.7.4 ([#1722](https://github.com/googleapis/java-spanner-jdbc/issues/1722)) ([1328213](https://github.com/googleapis/java-spanner-jdbc/commit/13282136921d0167c19cac38df0a652cc5477faa))
+* Update dependency org.springframework.boot:spring-boot to v3.3.2 ([#1718](https://github.com/googleapis/java-spanner-jdbc/issues/1718)) ([ede7211](https://github.com/googleapis/java-spanner-jdbc/commit/ede72113801de4a27492cf672a4c5e3edb37bc5e))
+* Update dependency org.springframework.boot:spring-boot to v3.3.3 ([#1723](https://github.com/googleapis/java-spanner-jdbc/issues/1723)) ([55112ac](https://github.com/googleapis/java-spanner-jdbc/commit/55112ac5f00d4a8d7726fa8bc5e9428d08d21227))
+* Update dependency org.springframework.boot:spring-boot-starter-data-jdbc to v3.3.3 ([#1724](https://github.com/googleapis/java-spanner-jdbc/issues/1724)) ([db60f4f](https://github.com/googleapis/java-spanner-jdbc/commit/db60f4f4f8713a30c1fe275266ff455fd03d84a4))
+* Update dependency org.springframework.boot:spring-boot-starter-parent to v3.3.3 ([#1725](https://github.com/googleapis/java-spanner-jdbc/issues/1725)) ([47fda8f](https://github.com/googleapis/java-spanner-jdbc/commit/47fda8f9b8cf639fe11fb3241256490f660e0d8b))
+* Update dependency org.springframework.data:spring-data-bom to v2024.0.3 ([#1704](https://github.com/googleapis/java-spanner-jdbc/issues/1704)) ([e82d839](https://github.com/googleapis/java-spanner-jdbc/commit/e82d8398eede11469c966aa11c2188c671a5f02b))
+* Update dependency org.testcontainers:testcontainers to v1.20.1 ([#1684](https://github.com/googleapis/java-spanner-jdbc/issues/1684)) ([0907305](https://github.com/googleapis/java-spanner-jdbc/commit/09073057df2cff41b7a62f56dc0cf57ed62f4801))
+
## [2.20.2](https://github.com/googleapis/java-spanner-jdbc/compare/v2.20.1...v2.20.2) (2024-08-07)
diff --git a/README.md b/README.md
index e7c6e9e6c..80eb44152 100644
--- a/README.md
+++ b/README.md
@@ -15,25 +15,32 @@ Java idiomatic client for [Google Cloud Spanner JDBC][product-docs].
If you are using Maven, add this to your pom.xml file:
+
```xml
com.google.cloud
google-cloud-spanner-jdbc
- 2.4.1
+ 2.21.0
```
+
+
If you are using Gradle without BOM, add this to your dependencies
+
```Groovy
-implementation 'com.google.cloud:google-cloud-spanner-jdbc:2.4.1'
+implementation 'com.google.cloud:google-cloud-spanner-jdbc:2.21.0'
```
+
If you are using SBT, add this to your dependencies
+
```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "2.4.1"
+libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "2.21.0"
```
+
## Authentication
diff --git a/pom.xml b/pom.xml
index 9f17e9c78..220e0dcea 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.20.2
+ 2.21.0
jar
Google Cloud Spanner JDBC
https://github.com/googleapis/java-spanner-jdbc
@@ -14,7 +14,7 @@
com.google.cloud
sdk-platform-java-config
- 3.33.0
+ 3.34.0
@@ -61,7 +61,7 @@
com.google.cloud
google-cloud-spanner-bom
- 6.72.0
+ 6.73.0
pom
import
@@ -166,7 +166,7 @@
org.testcontainers
testcontainers
- 1.19.8
+ 1.20.1
test
@@ -232,13 +232,13 @@
com.google.cloud
google-cloud-trace
- 2.47.0
+ 2.48.0
test
com.google.api.grpc
proto-google-cloud-trace-v1
- 2.47.0
+ 2.48.0
test
@@ -426,6 +426,32 @@
+
+
+ alt_build_dir
+
+
+ alt.build.dir
+
+
+
+ ${alt.build.dir}
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.6.0
+
+
+
+ ${alt.build.dir}/single.jar
+
+
+
+
+
+
+
@@ -433,7 +459,7 @@
org.apache.maven.plugins
maven-project-info-reports-plugin
- 3.6.2
+ 3.7.0
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index 87be572f4..522c41a61 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.20.1
+ 2.20.2
diff --git a/samples/pom.xml b/samples/pom.xml
index 31285be61..39d5d24c8 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -39,7 +39,7 @@
org.apache.maven.plugins
maven-deploy-plugin
- 3.1.2
+ 3.1.3
true
diff --git a/samples/quickperf/.gitignore b/samples/quickperf/.gitignore
new file mode 100644
index 000000000..1df5b5e0c
--- /dev/null
+++ b/samples/quickperf/.gitignore
@@ -0,0 +1,3 @@
+target
+.vscode
+.DS_Store
\ No newline at end of file
diff --git a/samples/quickperf/exampleconfigs/config.json b/samples/quickperf/exampleconfigs/config.json
new file mode 100644
index 000000000..b43c32e1c
--- /dev/null
+++ b/samples/quickperf/exampleconfigs/config.json
@@ -0,0 +1,9 @@
+{
+ "project": "xxxx",
+ "instance": "xxx",
+ "database": "xxx",
+ "threads": 1,
+ "iterations": 100,
+ "query": "SELECT 1",
+ "writeMetricToFile": false
+}
\ No newline at end of file
diff --git a/samples/quickperf/exampleconfigs/users/groupmgt_config.json b/samples/quickperf/exampleconfigs/users/groupmgt_config.json
new file mode 100644
index 000000000..8bc094513
--- /dev/null
+++ b/samples/quickperf/exampleconfigs/users/groupmgt_config.json
@@ -0,0 +1,13 @@
+{
+ "project": "xxx",
+ "instance": "xxx",
+ "database": "users",
+ "threads": 4,
+ "iterations": 250,
+ "query": "INSERT INTO GroupMgmt (group_id, grpname) VALUES(?,?)",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#i"},
+ {"order": 2, "value": "#s"}
+ ]
+}
\ No newline at end of file
diff --git a/samples/quickperf/exampleconfigs/users/loadtestusers.json b/samples/quickperf/exampleconfigs/users/loadtestusers.json
new file mode 100644
index 000000000..4a4a3aee8
--- /dev/null
+++ b/samples/quickperf/exampleconfigs/users/loadtestusers.json
@@ -0,0 +1,13 @@
+{
+ "project": "xxx",
+ "instance": "xxx",
+ "database": "users",
+ "threads": 1,
+ "iterations": 10,
+ "query": "SELECT users.user_id, membership.enrolled, GroupMgmt.grpname FROM users, GroupMgmt, membership WHERE users.user_id = ? AND users.user_id = membership.user_id AND GroupMgmt.group_id = membership.group_id",
+ "samplingQuery": "SELECT user_id FROM Users TABLESAMPLE RESERVOIR (100000 ROWS)",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#pi"}
+ ]
+}
\ No newline at end of file
diff --git a/samples/quickperf/exampleconfigs/users/membership_config.json b/samples/quickperf/exampleconfigs/users/membership_config.json
new file mode 100644
index 000000000..d3c56d101
--- /dev/null
+++ b/samples/quickperf/exampleconfigs/users/membership_config.json
@@ -0,0 +1,9 @@
+{
+ "project": "xxx",
+ "instance": "xxx",
+ "database": "users",
+ "threads": 1,
+ "iterations": 100,
+ "query": "INSERT INTO membership(user_id, group_id, enrolled) VALUES((SELECT user_id FROM Users TABLESAMPLE RESERVOIR (1 ROWS)), (SELECT group_id FROM GroupMgmt TABLESAMPLE RESERVOIR (1 ROWS)), CURRENT_TIMESTAMP())",
+ "writeMetricToFile": false
+}
\ No newline at end of file
diff --git a/samples/quickperf/exampleconfigs/users/run.sh b/samples/quickperf/exampleconfigs/users/run.sh
new file mode 100755
index 000000000..ac82643f8
--- /dev/null
+++ b/samples/quickperf/exampleconfigs/users/run.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# Generate Data
+cd ../..
+
+mvn -q compile
+
+mvn -q exec:java -Dexec.args="-c exampleconfigs/users/users_config.json"
+
+mvn -q exec:java -Dexec.args="-c exampleconfigs/users/groupmgt_config.json"
+
+mvn -q exec:java -Dexec.args="-c exampleconfigs/users/membership_config.json"
+
+# load test random users
+mvn -q exec:java -Dexec.args="-c exampleconfigs/users/loadtestusers.json"
diff --git a/samples/quickperf/exampleconfigs/users/users.ddl b/samples/quickperf/exampleconfigs/users/users.ddl
new file mode 100644
index 000000000..6498bb591
--- /dev/null
+++ b/samples/quickperf/exampleconfigs/users/users.ddl
@@ -0,0 +1,17 @@
+CREATE TABLE GroupMgmt (
+ group_id INT64,
+ grpname STRING(MAX),
+) PRIMARY KEY(group_id);
+
+CREATE TABLE Users (
+ user_id INT64,
+ name STRING(MAX),
+) PRIMARY KEY(user_id);
+
+CREATE TABLE membership (
+ user_id INT64,
+ group_id INT64,
+ enrolled TIMESTAMP NOT NULL OPTIONS (
+ allow_commit_timestamp = true
+ ),
+) PRIMARY KEY(user_id, group_id);
\ No newline at end of file
diff --git a/samples/quickperf/exampleconfigs/users/users_config.json b/samples/quickperf/exampleconfigs/users/users_config.json
new file mode 100644
index 000000000..6cdbbedc5
--- /dev/null
+++ b/samples/quickperf/exampleconfigs/users/users_config.json
@@ -0,0 +1,13 @@
+{
+ "project": "xxx",
+ "instance": "xxx",
+ "database": "users",
+ "threads": 1,
+ "iterations": 1000,
+ "query": "INSERT INTO Users (user_id, name) VALUES(?,?)",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#i"},
+ {"order": 2, "value": "#s"}
+ ]
+}
\ No newline at end of file
diff --git a/samples/quickperf/java.header b/samples/quickperf/java.header
new file mode 100644
index 000000000..d0970ba7d
--- /dev/null
+++ b/samples/quickperf/java.header
@@ -0,0 +1,15 @@
+^/\*$
+^ \* Copyright \d\d\d\d,? Google (Inc\.|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$
+^ \*$
+^ \*[ ]+https?://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\.$
+^ \*/$
diff --git a/samples/quickperf/license-checks.xml b/samples/quickperf/license-checks.xml
new file mode 100644
index 000000000..a7a611940
--- /dev/null
+++ b/samples/quickperf/license-checks.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/samples/quickperf/pom.xml b/samples/quickperf/pom.xml
new file mode 100644
index 000000000..181a7639f
--- /dev/null
+++ b/samples/quickperf/pom.xml
@@ -0,0 +1,100 @@
+
+
+
+ 4.0.0
+
+ com.google.cloud.jdbc.quickperf
+ jdbc-quickperf
+ 1.0.0
+ jdbc-quickperf
+
+ com.google.cloud
+ sdk-platform-java-config
+ 3.34.0
+
+
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+
+
+ com.google.cloud
+ libraries-bom
+ 26.44.0
+ pom
+ import
+
+
+
+
+
+
+ net.datafaker
+ datafaker
+ 2.3.1
+
+
+ com.google.cloud
+ google-cloud-spanner
+
+
+ commons-cli
+ commons-cli
+ 1.9.0
+
+
+ com.google.cloud
+ google-cloud-spanner-jdbc
+
+
+ org.apache.commons
+ commons-lang3
+ 3.16.0
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.17.2
+
+
+
+ org.testcontainers
+ testcontainers
+ 1.20.1
+ test
+
+
+ org.springframework.boot
+ spring-boot
+ 3.3.3
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.4.1
+
+ com.google.cloud.jdbc.quickperf.QuickPerf
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/quickperf/readme.md b/samples/quickperf/readme.md
new file mode 100644
index 000000000..675155b94
--- /dev/null
+++ b/samples/quickperf/readme.md
@@ -0,0 +1,302 @@
+# Introduction
+
+QuickPerf is a simple utility that uses JDBC to perform load testing on individual statements (such as queries and DML) against Spanner. It provides a rapid assessment of expected end-to-end latencies for specific statements, aiding in the performance tuning of schemas, indexes, and more. The tool includes random data generators to quickly fill a given schema with dummy data, respecting foreign-key relationships and interleaved tables.
+
+QuickPerf is not designed to replace comprehensive test suites like JMeter. Instead, it serves as a quick alternative for gaining performance insights or populating schemas.
+
+**Key Features**:
+* Multi-threading to simulate concurrency
+* Query parameterization with random value generators (String, Integer, Timestamp)
+* Sampling of records for seeding foreign-key relationships or testing against a specific subset of data
+* Batch mode support
+* Automatic statement and transaction tagging
+
+
+# Installation on Ubuntu
+```
+sudo apt-get install openjdk-8-jdk
+sudo apt install maven
+```
+
+## Authentification
+It is recommended to use a service account, otherwise larger scale tests will run into quota limitations
+
+Set active auth to service account:
+```
+gcloud auth list
+gcloud config set account xxx-compute@developer.gserviceaccount.com
+```
+
+# Configuration
+
+## Parameters
+```
+{
+ "project": "Project ID",
+ "instance": "Spanner Instance",
+ "database": "Spanner Database",
+ "threads": Number of concurrent threads,
+ "iterations": Number of how often a statement should be executed in a thread,
+ "query": "Statement (e.g. query)",
+ "samplingQuery": "OPTIONAL - Sampling query",
+ "writeMetricToFile": Will write latency metrics to a file (true/false),
+ "batchSize": If testing batching - determines how large a batch size would be,
+ "queryParams": [
+ {"order": 1, "value": "query paramters with value generator"}
+ ]
+}
+```
+
+## Example Config
+```
+{
+ "project": "xxxx",
+ "instance": "xxxx",
+ "database": "users",
+ "threads": 1,
+ "iterations": 10,
+ "query": "SELECT users.user_id, membership.enrolled, GroupMgmt.grpname FROM users, GroupMgmt, membership WHERE users.user_id = ? AND users.user_id = membership.user_id AND GroupMgmt.group_id = membership.group_id",
+ "samplingQuery": "SELECT user_id FROM Users TABLESAMPLE RESERVOIR (100000 ROWS)",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#pi"}
+ ]
+}
+```
+
+# Hello World Example
+
+The folder `exampleconfigs/config.json` contains a simple setup that runs SELECT 1 against the database
+
+Configure the right Spanner `project` and `instance` and run the app.
+
+**config.json**
+```
+{
+"project": "xxxx",
+"instance": "xxx",
+"database": "xxx",
+"threads": 1,
+"iterations": 100,
+"query": "SELECT 1",
+"writeMetricToFile": false,
+"batchSize": 0
+}
+```
+
+**Run:**
+```
+mvn -q exec:java -Dexec.args="-c exampleconfigs/config.json"
+```
+
+
+
+# End-to-End Example
+
+Generates three tables with n:m relationships and performs a load test.
+
+All in one runner generating test data and executing load test:
+```
+exampleconfigs/users/run.sh
+```
+
+What needs to be done to run it:
+* Create spanner instance
+* Create database named `users`
+* Set `project` and `instance` in each of the config JSON files located under `exampleconfigs/users/users_config.json`
+ * `exampleconfigs/users/users_config.json`
+ * `exampleconfigs/users/groupmgt_config.json`
+ * `exampleconfigs/users/membership_config.json`
+ * `exampleconfigs/users/loadtestusers.json`
+
+
+**Generate users table:**
+
+```
+mvn -q exec:java -Dexec.args="-c exampleconfigs/users/users_config.json"
+```
+
+users_config.json:
+```
+{
+ "project": "xxxx",
+ "instance": "xxxx",
+ "database": "users",
+ "threads": 4,
+ "iterations": 1000,
+ "query": "INSERT INTO Users (user_id, name) VALUES(?,?)",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#i"},
+ {"order": 2, "value": "#s"}
+ ]
+}
+```
+
+**Generate GroupMgmt table:**
+
+```
+mvn -q exec:java -Dexec.args="-c exampleconfigs/users/groupmgt_config.json"
+```
+
+groupmgt_config.json
+```
+{
+ "project": "xxxx",
+ "instance": "xxxx",
+ "database": "users",
+ "threads": 4,
+ "iterations": 1000,
+ "query": "INSERT INTO GroupMgmt (group_id, grpname) VALUES(?,?)",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#i"},
+ {"order": 2, "value": "#s"}
+ ]
+}
+```
+
+**Generate Membership table:**
+
+Run:
+```
+mvn -q exec:java -Dexec.args="-c exampleconfigs/users/membership_config.json"
+```
+
+```
+{
+ "project": "xxxx",
+ "instance": "xxxx",
+ "database": "users",
+ "threads": 1,
+ "iterations": 100,
+ "query": "INSERT INTO membership(user_id, group_id, enrolled) VALUES((SELECT user_id FROM Users TABLESAMPLE RESERVOIR (1 ROWS)), (SELECT group_id FROM GroupMgmt TABLESAMPLE RESERVOIR (1 ROWS)), CURRENT_TIMESTAMP())",
+ "writeMetricToFile": false
+}
+```
+
+
+Load test random users
+```
+mvn -q exec:java -Dexec.args="-c exampleconfigs/users/loadtestusers.json"
+```
+
+# Randomization examples
+
+## String
+Will generate a different random String value for each #s
+```
+INSERT INTO transactions (id, name, ts) VALUES (#s, #s, CURRENT_TIMESTAMP())
+```
+```
+{
+ "project": "xxxx",
+ "instance": "xxxx",
+ "database": "xxxx",
+ "threads": 4,
+ "iterations": 1000,
+ "query": "INSERT INTO transactions (id, name, ts) VALUES (?, ?, CURRENT_TIMESTAMP())",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#s"},
+ {"order": 2, "value": "#s"}
+ ]
+}
+```
+
+```
+SELECT * FROM transactions WHERE id=#s
+```
+```
+{
+ "project": "xxxx",
+ "instance": "xxxx",
+ "database": "xxxx",
+ "threads": 4,
+ "iterations": 1000,
+ "query": "SELECT * FROM transactions WHERE id=?",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#s"},
+ ]
+}
+```
+
+
+## Integer
+Will generate a different random value for each #i
+```
+UPDATE accounts SET cid=#i WHERE aaId=4
+```
+
+```
+{
+ "project": "xxxx",
+ "instance": "xxxx",
+ "database": "xxxx",
+ "threads": 4,
+ "iterations": 1000,
+ "query": "UPDATE accounts SET ? WHERE aaId=4",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#i"},
+ ]
+}
+```
+
+## Integer min max
+Generates #i(1,10) integer values between 1 and 10
+```
+INSERT INTO test (id, groupid, amount) VALUES (#s, #i(1,2)#, #i)
+```
+
+## Timestamp
+Workaround for not randomizing timestamps is to use current_timestsamp()
+```
+INSERT INTO transactions (id, name, ts) VALUES (#s, #s, CURRENT_TIMESTAMP())'
+```
+```
+{
+ "project": "xxxx",
+ "instance": "xxxx",
+ "database": "xxxx",
+ "threads": 4,
+ "iterations": 1000,
+ "query": "INSERT INTO transactions (id, name, ts) VALUES (?, ?, CURRENT_TIMESTAMP())'",
+ "writeMetricToFile": false,
+ "queryParams": [
+ {"order": 1, "value": "#s"},
+ {"order": 1, "value": "#s"}
+ ]
+}
+```
+
+## Sampling IDs
+Sometimes it might be required to sample existing IDs that are then used in the query that is executed.
+The parameter ```-s``` allows to pull a sampled dataset, but only one column is allowed in the resultset.
+This query is executed only once and before the beginning of the run and the dataset is reused across the threads.
+
+* #ps will add quotes - such as for Strings
+* #pi will **not** add quotes such as when integers are used
+
+In this case the #ps is the placeholder for samples that are pulled from the -s parameter
+```
+-q 'SELECT * FROM test WHERE id = #ps''
+-s 'SELECT id FROM test TABLESAMPLE RESERVOIR (100 ROWS)'
+```
+
+## Many-to-Many Relationship Example
+Insert Users
+```
+INSERT INTO Users (user_id, name) VALUES(#i,#s)
+```
+
+Insert Groups
+```
+INSERT INTO GroupMgmt (group_id, grpname) VALUES(#i,#s)
+```
+
+Insert relationships with sampling:
+```
+INSERT INTO membership(user_id, group_id, enrolled) VALUES((SELECT user_id FROM Users TABLESAMPLE RESERVOIR (1 ROWS)), (SELECT group_id FROM GroupMgmt TABLESAMPLE RESERVOIR (1 ROWS)), CURRENT_TIMESTAMP())
+```
\ No newline at end of file
diff --git a/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/ProgressTracker.java b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/ProgressTracker.java
new file mode 100644
index 000000000..90b241b1d
--- /dev/null
+++ b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/ProgressTracker.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2024 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.jdbc.quickperf;
+
+import java.util.List;
+
+public class ProgressTracker extends Thread {
+ private static final int SLEEP_TIME_INIT = 2000;
+ private static final int SLEEP_TIME_POLL = 200;
+
+ private final List threadList;
+
+ private final int maxIt;
+ private int currentIt = 0;
+
+ public ProgressTracker(List threadList, int maxIt) {
+ this.threadList = threadList;
+ this.maxIt = maxIt;
+ }
+
+ public void run() {
+ sleep(SLEEP_TIME_INIT);
+ while (currentIt < maxIt) {
+ currentIt = 0;
+ for (QuickPerfRunner thread : threadList) {
+ currentIt = currentIt + thread.getProgress();
+
+ int percent = (int) Math.ceil(((double) currentIt / maxIt) * 100.0);
+ print_progress(percent);
+ }
+
+ if (sleep(SLEEP_TIME_POLL)) {
+ break;
+ }
+ }
+ print_progress(100);
+ }
+
+ public void print_progress(int percent) {
+ StringBuilder bar = new StringBuilder("Progress: [");
+
+ for (int i = 0; i < 50; i++) {
+ if (i < (percent / 2)) {
+ bar.append("=");
+ } else if (i == (percent / 2)) {
+ bar.append(">");
+ } else {
+ bar.append(" ");
+ }
+ }
+
+ bar.append("] ").append(percent).append("% ");
+ System.out.print("\r" + bar);
+ }
+
+ private boolean sleep(int sleeptime) {
+ try {
+ Thread.sleep(sleeptime);
+ } catch (InterruptedException e) {
+ System.err.println("Progress tracker thread interrupted");
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/QuickPerf.java b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/QuickPerf.java
new file mode 100644
index 000000000..9eb0fc45c
--- /dev/null
+++ b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/QuickPerf.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2024 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.jdbc.quickperf;
+
+import com.google.cloud.jdbc.quickperf.config.Config;
+import com.google.cloud.jdbc.quickperf.config.ConfigParser;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.lang3.ArrayUtils;
+
+public class QuickPerf extends Thread {
+
+ private static final String BREAK_STR =
+ "###################################################################################################";
+
+ // TODO: make measurement file configurable
+ private static final String MEASURES_FILE_NAME = "measures.txt";
+
+ public static void main(String[] args) throws Exception {
+ Options options = new Options();
+
+ options.addOption(QuickPerf.addOption("c", "config", true, "Config File"));
+
+ CommandLineParser parser = new DefaultParser();
+ HelpFormatter formatter = new HelpFormatter();
+ CommandLine cmd = null;
+
+ ZonedDateTime testStartTimestamp = ZonedDateTime.now();
+
+ try {
+ cmd = parser.parse(options, args);
+ } catch (ParseException e) {
+ System.out.println(e.getMessage());
+ formatter.printHelp("utility-name", options);
+
+ System.exit(1);
+ }
+
+ Config config = ConfigParser.parseConfigFile(cmd.getOptionValue("config"));
+
+ float[] measures = new float[config.getIterations() * config.getThreads()];
+
+ // initialize threads (for sampling if present)
+ List threadList = new ArrayList();
+ for (int i = 0; i < config.getThreads(); i++) {
+ QuickPerfRunner thread = new QuickPerfRunner(config);
+ if (config.getSamplingQuery() != null) {
+ thread.runSampling();
+ }
+ threadList.add(thread);
+ }
+
+ // start threads
+ for (QuickPerfRunner thread : threadList) {
+ thread.start();
+ }
+
+ // ProgressBar Tracker Thread
+ ProgressTracker progressTracker =
+ progressTracker =
+ new ProgressTracker(threadList, config.getIterations() * config.getThreads());
+
+ progressTracker.start();
+ progressTracker.join();
+
+ int i = 0;
+ for (QuickPerfRunner thread : threadList) {
+ thread.join();
+
+ if (i == 0) {
+ measures = thread.getMeasures();
+ } else {
+ measures = ArrayUtils.addAll(measures, thread.getMeasures());
+ }
+ i++;
+ }
+
+ // write to file before its sorted
+ if (config.getWriteMetricToFile()) {
+ try {
+ writeMeasuresToFile(measures, MEASURES_FILE_NAME);
+ } catch (IOException e) {
+ System.err.println("An error occurred while writing the file: " + e.getMessage());
+ }
+ }
+
+ System.out.println("\n" + BREAK_STR);
+ System.out.println("Query: " + config.getQuery());
+ System.out.println("Params: " + config.paramsToString());
+ System.out.println("Tag: " + Config.DEFAULT_TAG);
+ if (config.getBatchSize() > 0) {
+ System.out.println("Batching Enabled (size): " + config.getBatchSize());
+ }
+ System.out.printf("Start: %s End: %s%n", testStartTimestamp, ZonedDateTime.now());
+ System.out.printf(
+ "Finished with a total of %s runs across %s Threads.\nLatencies (ms): p50 = %s, p95 = %s, p99 = %s, min = %s, max = %s%n",
+ config.getIterations() * config.getThreads(),
+ config.getThreads(),
+ calcPerc(measures, 50),
+ calcPerc(measures, 95),
+ calcPerc(measures, 99),
+ getMin(measures),
+ getMax(measures));
+ System.out.println(BREAK_STR);
+ }
+
+ public static Option addOption(String option, String longOption, boolean hasArgs, String desc) {
+ Option opt = new Option(option, longOption, hasArgs, desc);
+ opt.setRequired(true);
+
+ return opt;
+ }
+
+ public static Option addOption(
+ String option, String longOption, boolean hasArgs, String desc, boolean required) {
+ Option opt = new Option(option, longOption, hasArgs, desc);
+ opt.setRequired(required);
+
+ return opt;
+ }
+
+ public static float calcPerc(float[] nums, double percentile) {
+ int n = nums.length;
+ Arrays.sort(nums);
+
+ double index = (percentile / 100) * (n - 1);
+
+ if (index == Math.floor(index)) {
+ return nums[(int) index];
+ } else {
+ int lowerIndex = (int) Math.floor(index);
+ int upperIndex = (int) Math.ceil(index);
+ float lowerValue = nums[lowerIndex];
+ float upperValue = nums[upperIndex];
+ return (float) ((1 - (index - lowerIndex)) * lowerValue + (index - lowerIndex) * upperValue);
+ }
+ }
+
+ public static float getMax(float[] measures) {
+ if (measures == null || measures.length == 0) {
+ throw new IllegalArgumentException("Array is null or empty");
+ }
+
+ Arrays.sort(measures);
+ return measures[measures.length - 1];
+ }
+
+ public static float getMin(float[] measures) {
+ if (measures == null || measures.length == 0) {
+ throw new IllegalArgumentException("Array is null or empty");
+ }
+
+ Arrays.sort(measures);
+ return measures[0];
+ }
+
+ public static void writeMeasuresToFile(float[] array, String fileName) throws IOException {
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
+ for (float value : array) {
+ writer.write(Float.toString(value));
+ writer.newLine();
+ }
+ }
+ }
+}
diff --git a/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/QuickPerfRunner.java b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/QuickPerfRunner.java
new file mode 100644
index 000000000..84e2bc9ac
--- /dev/null
+++ b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/QuickPerfRunner.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2024 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.jdbc.quickperf;
+
+import com.google.cloud.jdbc.quickperf.config.Config;
+import com.google.cloud.jdbc.quickperf.config.QueryParam;
+import com.google.cloud.spanner.Dialect;
+import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection;
+import java.security.SecureRandom;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.Random;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import net.datafaker.Faker;
+
+public class QuickPerfRunner extends Thread {
+ private static final Properties DEFAULT_PROPERTIES = new Properties();
+
+ // perf measurement
+ private float[] measures;
+
+ private final List sampledValueList = new ArrayList();
+ private final Config config;
+
+ private int progress;
+
+ public QuickPerfRunner(Config config) {
+ this.config = config;
+ }
+
+ public void runSampling() {
+ System.out.println("Running Sampling... ");
+
+ try (Connection connection = createConnection(config)) {
+ try (Statement statement = connection.createStatement()) {
+ boolean hasResults = statement.execute(config.getSamplingQuery());
+
+ if (!hasResults) {
+ System.out.println("Nothing sampled");
+ return;
+ }
+
+ ResultSet rs = statement.getResultSet();
+ while (rs.next()) {
+ String value = rs.getString(1);
+ sampledValueList.add(value);
+ }
+
+ System.out.printf("Finished sampling %s records%n", sampledValueList.size());
+ } catch (SQLException e) {
+ //noinspection CallToPrintStackTrace
+ e.printStackTrace();
+ }
+
+ } catch (SQLException e) {
+ //noinspection CallToPrintStackTrace
+ e.printStackTrace();
+ }
+ }
+
+ private Connection createConnection(Config config) throws SQLException {
+ String connectionUrl = createConnectionURL(config);
+ Properties properties = createConnectionProperties();
+ return DriverManager.getConnection(connectionUrl, properties);
+ }
+
+ private String createConnectionURL(Config config) {
+ if (config.isIsEmulator()) {
+ return String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s?autoConfigEmulator=true",
+ config.getProject(), config.getInstance(), config.getDatabase());
+ } else {
+ return String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ config.getProject(), config.getInstance(), config.getDatabase());
+ }
+ }
+
+ private Properties createConnectionProperties() {
+ if (System.getProperty("spanner.host") != null) {
+ Properties properties = new Properties();
+ properties.setProperty("endpoint", System.getProperty("spanner.host"));
+ return properties;
+ }
+ return DEFAULT_PROPERTIES;
+ }
+
+ public void run() {
+ if (config.getBatchSize() > 0) {
+ int val = (int) Math.ceil((double) config.getIterations() / config.getBatchSize());
+ measures = new float[val];
+ } else {
+ measures = new float[config.getIterations()];
+ }
+
+ try (Connection connection = createConnection(config)) {
+
+ // determine database dialect to set right tagging syntax
+ boolean isGoogleSQL =
+ connection
+ .unwrap(CloudSpannerJdbcConnection.class)
+ .getDialect()
+ .equals(Dialect.GOOGLE_STANDARD_SQL);
+ String tagPrefix = isGoogleSQL ? "" : "SPANNER.";
+
+ connection.setAutoCommit(false);
+
+ // if there is DML switch to r/w transaction mode and apply transaction tagging.
+ // Otherwise set to read-only mode.
+ if (config.getQuery().contains("INSERT")
+ || config.getQuery().contains("UPDATE")
+ || config.getQuery().contains("DELETE")) {
+ // read-write
+ connection.createStatement().execute("SET TRANSACTION READ WRITE");
+ connection
+ .createStatement()
+ .execute(String.format("SET %sTRANSACTION_TAG = '%s'", tagPrefix, config.DEFAULT_TAG));
+
+ } else {
+ // read-only
+ // connection.createStatement().execute("SET TRANSACTION READ ONLY");
+ connection.setAutoCommit(true);
+ }
+
+ PreparedStatement statement = connection.prepareStatement(config.getQuery());
+ int batchCounter = config.getBatchSize();
+ int batchRound = 0;
+
+ for (int i = 0; i < config.getIterations(); i++) {
+ if (config.getBatchSize() == 0) {
+ // single statements
+ try {
+ if (config.getQueryParams() != null) {
+ parametrizeStatement(statement, config.getQueryParams());
+ }
+ connection
+ .createStatement()
+ .execute(String.format("SET %sSTATEMENT_TAG='%s'", tagPrefix, config.DEFAULT_TAG));
+
+ long start = System.nanoTime();
+ boolean hasResults = statement.execute();
+ if (!connection.getAutoCommit()) {
+ connection.commit();
+ }
+ long stop = System.nanoTime() - start;
+
+ if (hasResults) {
+ statement.getResultSet().close();
+ }
+
+ measures[i] = (float) stop / 1000000;
+ progress++;
+ } catch (Exception e) {
+ if (e.getMessage().contains("ALREADY_EXISTS:")) {
+ System.out.println("duplicate key - retry");
+ i--;
+ } else {
+ throw e;
+ }
+ }
+ } else if (config.getQuery().contains("INSERT")
+ || config.getQuery().contains("UPDATE")
+ || config.getQuery().contains("DELETE")) {
+ // batching
+ try {
+ if (config.getQueryParams() != null) {
+ parametrizeStatement(statement, config.getQueryParams());
+ }
+
+ statement.addBatch();
+
+ if (batchCounter == 0 || i == config.getIterations() - 1) {
+ connection
+ .createStatement()
+ .execute(
+ String.format("SET %sSTATEMENT_TAG='%s'", tagPrefix, config.DEFAULT_TAG));
+
+ long start = System.nanoTime();
+ statement.executeBatch();
+ if (!connection.getAutoCommit()) {
+ connection.commit();
+ }
+ long stop = System.nanoTime() - start;
+
+ batchCounter = config.getBatchSize();
+
+ measures[batchRound] = (float) stop / 1000000;
+ batchRound++;
+ }
+
+ progress++;
+ batchCounter--;
+ } catch (Exception e) {
+ if (e.getMessage().contains("ALREADY_EXISTS:")) {
+ System.out.println("duplicate key - retry");
+ i--;
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ System.out.println(
+ "Batching is only allowed for DML. Set batchSize=0 to disable batching.");
+ System.exit(1);
+ }
+ }
+ } catch (SQLException e) {
+ //noinspection CallToPrintStackTrace
+ e.printStackTrace();
+ }
+ }
+
+ public static float[] appendFloatArray(float[] originalArray, float[] elementsToAppend) {
+ int originalLength = originalArray.length;
+ int elementsLength = elementsToAppend.length;
+
+ float[] resultArray = new float[originalLength + elementsLength];
+ System.arraycopy(originalArray, 0, resultArray, 0, originalLength);
+ System.arraycopy(elementsToAppend, 0, resultArray, originalLength, elementsLength);
+
+ return resultArray;
+ }
+
+ private void parametrizeStatement(PreparedStatement statement, List paramList)
+ throws SQLException {
+ for (QueryParam param : paramList) {
+ if (param.getValue().contains("#i")) {
+ // integer plus integer with custom range
+ int value = replaceInt(param.getValue());
+ statement.setInt(param.getOrder(), value);
+ } else if (param.getValue().contains("#d")) {
+ // double
+ Double value = replaceDouble(param.getValue());
+ statement.setDouble(param.getOrder(), value);
+ } else if (param.getValue().contains("#s")) {
+ // String
+ String value = replaceString(param.getValue());
+ statement.setString(param.getOrder(), value);
+ } else if (param.getValue().contains("#ps")) {
+ // Sampled Query - String
+ String value = replaceSampleQueryString(param.getValue());
+ statement.setString(param.getOrder(), value);
+ } else if (param.getValue().contains("#pi")) {
+ // Sampled Query - Integer
+ Long value = replaceSampleQueryInt(param.getValue());
+ statement.setLong(param.getOrder(), value);
+ }
+ }
+ }
+
+ private int replaceInt(String value) {
+ Faker f = new Faker();
+ // integer with min, max
+ String pattern = "#i\\((\\d+),(\\d+)\\)#";
+ Pattern regexPattern = Pattern.compile(pattern);
+ Matcher matcher = regexPattern.matcher(value);
+
+ if (matcher.find()) {
+ int min = Integer.parseInt(matcher.group(1));
+ int max = Integer.parseInt(matcher.group(2));
+
+ return f.number().numberBetween(min, max);
+ }
+
+ return Integer.parseInt(
+ value.replaceFirst("#i", String.valueOf(new SecureRandom().nextInt(Integer.MAX_VALUE))));
+ }
+
+ private Double replaceDouble(String value) {
+ Faker f = new Faker();
+
+ return Double.valueOf(
+ value.replaceFirst("#d", String.valueOf(f.number().randomDouble(2, 0, 999999999))));
+ }
+
+ private String replaceString(String value) {
+ return value.replaceFirst("#s", UUID.randomUUID().toString());
+ }
+
+ private String replaceSampleQueryString(String value) {
+ int randomIndex = new Random().nextInt(sampledValueList.size());
+ return value.replaceFirst("#ps", sampledValueList.get(randomIndex));
+ }
+
+ private Long replaceSampleQueryInt(String value) {
+ int randomIndex = new Random().nextInt(sampledValueList.size());
+
+ return Long.parseLong(value.replaceFirst("#pi", sampledValueList.get(randomIndex)));
+ }
+
+ public int getProgress() {
+ return progress;
+ }
+
+ public float[] getMeasures() {
+ return measures;
+ }
+}
diff --git a/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/config/Config.java b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/config/Config.java
new file mode 100644
index 000000000..b869be386
--- /dev/null
+++ b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/config/Config.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2024 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.jdbc.quickperf.config;
+
+import java.util.List;
+import java.util.Random;
+
+public class Config {
+ public static String DEFAULT_TAG = "perftest_" + (new Random()).nextInt(300);
+
+ private String project;
+ private String instance;
+ private String database;
+ private int threads;
+ private int iterations;
+ private String query;
+ private String samplingQuery;
+ private boolean writeMetricToFile;
+ private int batchSize;
+ private boolean isEmulator;
+ private List queryParams;
+
+ public String paramsToString() {
+ StringBuilder retVal = new StringBuilder();
+
+ if (queryParams != null) {
+
+ for (QueryParam param : queryParams) {
+ retVal.append(String.format("%s:%s ", param.getOrder(), param.getValue()));
+ }
+ }
+
+ return retVal.toString();
+ }
+
+ public int getBatchSize() {
+ return this.batchSize;
+ }
+
+ public void setBatchSize(int batchSize) {
+ this.batchSize = batchSize;
+ }
+
+ public String getProject() {
+ return project;
+ }
+
+ public void setProject(String project) {
+ this.project = project;
+ }
+
+ public String getInstance() {
+ return instance;
+ }
+
+ public void setInstance(String instance) {
+ this.instance = instance;
+ }
+
+ public String getDatabase() {
+ return database;
+ }
+
+ public void setDatabase(String database) {
+ this.database = database;
+ }
+
+ public int getThreads() {
+ return threads;
+ }
+
+ public void setThreads(int threads) {
+ this.threads = threads;
+ }
+
+ public int getIterations() {
+ return iterations;
+ }
+
+ public void setIterations(int iterations) {
+ this.iterations = iterations;
+ }
+
+ public String getQuery() {
+ return query;
+ }
+
+ public void setQuery(String query) {
+ this.query = query;
+ }
+
+ public boolean isWriteMetricToFile() {
+ return writeMetricToFile;
+ }
+
+ public void setWriteMetricToFile(boolean writeMetricToFile) {
+ this.writeMetricToFile = writeMetricToFile;
+ }
+
+ public List getQueryParams() {
+ return queryParams;
+ }
+
+ public void setQueryParams(List queryParams) {
+ this.queryParams = queryParams;
+ }
+
+ public String getSamplingQuery() {
+ return this.samplingQuery;
+ }
+
+ public void setSamplingQuery(String samplingQuery) {
+ this.samplingQuery = samplingQuery;
+ }
+
+ public boolean getWriteMetricToFile() {
+ return this.writeMetricToFile;
+ }
+
+ public boolean isIsEmulator() {
+ return this.isEmulator;
+ }
+
+ public boolean getIsEmulator() {
+ return this.isEmulator;
+ }
+
+ public void setIsEmulator(boolean isEmulator) {
+ this.isEmulator = isEmulator;
+ }
+}
diff --git a/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/config/ConfigParser.java b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/config/ConfigParser.java
new file mode 100644
index 000000000..8f056d114
--- /dev/null
+++ b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/config/ConfigParser.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 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.jdbc.quickperf.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.File;
+import java.io.IOException;
+
+public class ConfigParser {
+
+ public static Config parseConfigFile(String configFile) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+
+ return mapper.readValue(new File(configFile), Config.class);
+ }
+}
diff --git a/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/config/QueryParam.java b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/config/QueryParam.java
new file mode 100644
index 000000000..52807eb49
--- /dev/null
+++ b/samples/quickperf/src/main/java/com/google/cloud/jdbc/quickperf/config/QueryParam.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2024 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.jdbc.quickperf.config;
+
+public class QueryParam {
+ private int order;
+ private String value;
+
+ public int getOrder() {
+ return order;
+ }
+
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/samples/quickperf/src/test/java/com/google/cloud/jdbc/quickperf/AppTest.java b/samples/quickperf/src/test/java/com/google/cloud/jdbc/quickperf/AppTest.java
new file mode 100644
index 000000000..c147ebaa9
--- /dev/null
+++ b/samples/quickperf/src/test/java/com/google/cloud/jdbc/quickperf/AppTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2023 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.jdbc.quickperf;
+
+import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.google.cloud.spanner.connection.SpannerPool;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+import javax.annotation.Nonnull;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.springframework.boot.SpringApplication;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+
+public class AppTest {
+
+ private static final String TEST_FILE = "src/test/resources/testfile.json";
+
+ private static GenericContainer> emulator;
+
+ private static final String projectId = "test";
+ private static final String instanceId = "test";
+ private static final String databaseId = "quickperftest";
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ System.out.println("Starting Emulator");
+ emulator =
+ new GenericContainer<>(
+ DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:latest"))
+ .withExposedPorts(9010)
+ .waitingFor(Wait.forListeningPort());
+
+ emulator.start();
+ System.out.println("Finished starting Emulator");
+
+ List ddlList =
+ Arrays.asList(
+ "CREATE TABLE GroupMgmt ("
+ + "group_id INT64,"
+ + "grpname STRING(MAX),"
+ + ") PRIMARY KEY(group_id)",
+ "CREATE TABLE Users ("
+ + "user_id INT64,"
+ + "name STRING(MAX),"
+ + ") PRIMARY KEY(user_id)",
+ "CREATE TABLE membership ("
+ + "user_id INT64,"
+ + "group_id INT64,"
+ + "enrolled TIMESTAMP NOT NULL OPTIONS ("
+ + " allow_commit_timestamp = true"
+ + "),"
+ + ") PRIMARY KEY(user_id, group_id)");
+ try (Connection connection = createConnection();
+ Statement statement = connection.createStatement()) {
+ for (String ddl : ddlList) {
+ statement.addBatch(ddl);
+ }
+ statement.executeBatch();
+ }
+ // create test file
+ ProjectConfig projectConfig = createProjectConfig();
+
+ // Write the JSON to a file
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(SerializationFeature.INDENT_OUTPUT);
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+
+ File file = new File(TEST_FILE);
+ mapper.writeValue(file, projectConfig);
+ }
+
+ @Nonnull
+ private static ProjectConfig createProjectConfig() {
+ ProjectConfig projectConfig = new ProjectConfig();
+ projectConfig.setProject(projectId);
+ projectConfig.setInstance(instanceId);
+ projectConfig.setDatabase(databaseId);
+ projectConfig.setThreads(1);
+ projectConfig.setIterations(1000);
+ projectConfig.setQuery("INSERT INTO Users (user_id, name) VALUES(?,?)");
+ projectConfig.setWriteMetricToFile(false);
+ projectConfig.setIsEmulator(true);
+
+ QueryParam param1 = new QueryParam(1, "#i");
+ QueryParam param2 = new QueryParam(2, "#s");
+ projectConfig.setQueryParams(Arrays.asList(param1, param2));
+ return projectConfig;
+ }
+
+ @AfterClass
+ public static void cleanup() throws IOException {
+ // Close all Spanner connections.
+ SpannerPool.closeSpannerPool();
+
+ // Write an empty test file
+ Path path = Paths.get(TEST_FILE);
+ Files.newBufferedWriter(path, TRUNCATE_EXISTING).close();
+
+ // Stop the emulator.
+ emulator.stop();
+ }
+
+ private static Connection createConnection() throws SQLException {
+ String url =
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s?autoConfigEmulator=true",
+ projectId, instanceId, databaseId);
+ Properties properties = new Properties();
+ properties.put("endpoint", "localhost:" + emulator.getMappedPort(9010));
+ return DriverManager.getConnection(url, properties);
+ }
+
+ @Test
+ public void testRunApplication() throws Exception {
+
+ System.setProperty("spanner.emulator", "true");
+ System.setProperty("spanner.host", "localhost:" + emulator.getMappedPort(9010));
+ SpringApplication.run(AppTest.class).close();
+
+ String[] userConfig = {"-c" + TEST_FILE};
+ QuickPerf.main(userConfig);
+
+ try (Connection connection = createConnection()) {
+ testQuery(connection, "SELECT count(*) FROM Users", 1000);
+ }
+ }
+
+ private void testQuery(Connection connection, String query, int expected) throws SQLException {
+ try (Statement statement = connection.createStatement()) {
+ boolean hasResults = statement.execute(query);
+ assertTrue(hasResults);
+
+ ResultSet rs = statement.getResultSet();
+ while (rs.next()) {
+ int value = rs.getInt(1);
+ assertEquals(expected, value);
+ }
+ }
+ }
+}
diff --git a/samples/quickperf/src/test/java/com/google/cloud/jdbc/quickperf/ProjectConfig.java b/samples/quickperf/src/test/java/com/google/cloud/jdbc/quickperf/ProjectConfig.java
new file mode 100644
index 000000000..372d100fd
--- /dev/null
+++ b/samples/quickperf/src/test/java/com/google/cloud/jdbc/quickperf/ProjectConfig.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2023 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.jdbc.quickperf;
+
+import java.util.List;
+
+public class ProjectConfig {
+ private String project;
+ private String instance;
+ private String database;
+ private int threads;
+ private int iterations;
+ private String query;
+ private boolean writeMetricToFile;
+ private boolean isEmulator;
+ private List queryParams;
+
+ // Getters and setters
+
+ public String getProject() {
+ return project;
+ }
+
+ public void setProject(String project) {
+ this.project = project;
+ }
+
+ public String getInstance() {
+ return instance;
+ }
+
+ public void setInstance(String instance) {
+ this.instance = instance;
+ }
+
+ public String getDatabase() {
+ return database;
+ }
+
+ public void setDatabase(String database) {
+ this.database = database;
+ }
+
+ public int getThreads() {
+ return threads;
+ }
+
+ public void setThreads(int threads) {
+ this.threads = threads;
+ }
+
+ public int getIterations() {
+ return iterations;
+ }
+
+ public void setIterations(int iterations) {
+ this.iterations = iterations;
+ }
+
+ public String getQuery() {
+ return query;
+ }
+
+ public void setQuery(String query) {
+ this.query = query;
+ }
+
+ public boolean isWriteMetricToFile() {
+ return writeMetricToFile;
+ }
+
+ public void setWriteMetricToFile(boolean writeMetricToFile) {
+ this.writeMetricToFile = writeMetricToFile;
+ }
+
+ public List getQueryParams() {
+ return queryParams;
+ }
+
+ public void setQueryParams(List queryParams) {
+ this.queryParams = queryParams;
+ }
+
+ public boolean getWriteMetricToFile() {
+ return this.writeMetricToFile;
+ }
+
+ public boolean isIsEmulator() {
+ return this.isEmulator;
+ }
+
+ public boolean getIsEmulator() {
+ return this.isEmulator;
+ }
+
+ public void setIsEmulator(boolean isEmulator) {
+ this.isEmulator = isEmulator;
+ }
+}
diff --git a/samples/quickperf/src/test/java/com/google/cloud/jdbc/quickperf/QueryParam.java b/samples/quickperf/src/test/java/com/google/cloud/jdbc/quickperf/QueryParam.java
new file mode 100644
index 000000000..dcc0d85d4
--- /dev/null
+++ b/samples/quickperf/src/test/java/com/google/cloud/jdbc/quickperf/QueryParam.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 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.jdbc.quickperf;
+
+public class QueryParam {
+ private int order;
+ private String value;
+
+ public QueryParam(int order, String value) {
+ this.order = order;
+ this.value = value;
+ }
+
+ // Getters and setters
+
+ public int getOrder() {
+ return order;
+ }
+
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/samples/quickperf/src/test/resources/testfile.json b/samples/quickperf/src/test/resources/testfile.json
new file mode 100644
index 000000000..e69de29bb
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index ef21adcf1..751a517e9 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -28,7 +28,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.20.2
+ 2.21.0
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index ee6deda8b..2d0ed2811 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -9,7 +9,7 @@
com.google.cloud
sdk-platform-java-config
- 3.33.0
+ 3.34.0
@@ -26,7 +26,7 @@
com.google.cloud
libraries-bom
- 26.43.0
+ 26.44.0
pom
import
@@ -48,7 +48,7 @@
org.testcontainers
testcontainers
- 1.19.8
+ 1.20.1
test
diff --git a/samples/spring-data-jdbc/pom.xml b/samples/spring-data-jdbc/pom.xml
index f2d20d8ac..cf4c573b7 100644
--- a/samples/spring-data-jdbc/pom.xml
+++ b/samples/spring-data-jdbc/pom.xml
@@ -23,28 +23,28 @@
org.springframework.data
spring-data-bom
- 2024.0.2
+ 2024.0.3
import
pom
com.google.cloud
google-cloud-spanner-bom
- 6.71.0
+ 6.73.0
import
pom
com.google.cloud
libraries-bom
- 26.43.0
+ 26.44.0
import
pom
io.opentelemetry
opentelemetry-bom
- 1.40.0
+ 1.41.0
pom
import
@@ -55,19 +55,19 @@
org.springframework.boot
spring-boot-starter-data-jdbc
- 3.3.2
+ 3.3.3
com.google.cloud
google-cloud-spanner-jdbc
- 2.20.1
+ 2.20.2
org.postgresql
postgresql
- 42.7.3
+ 42.7.4
@@ -109,7 +109,7 @@
org.testcontainers
testcontainers
- 1.19.8
+ 1.20.1
test
@@ -119,7 +119,7 @@
com.spotify.fmt
fmt-maven-plugin
- 2.23
+ 2.24
diff --git a/samples/spring-data-mybatis/pom.xml b/samples/spring-data-mybatis/pom.xml
index a5ad7633d..8a13c6bb8 100644
--- a/samples/spring-data-mybatis/pom.xml
+++ b/samples/spring-data-mybatis/pom.xml
@@ -13,7 +13,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.3.2
+ 3.3.3
@@ -28,14 +28,14 @@
org.springframework.data
spring-data-bom
- 2024.0.2
+ 2024.0.3
import
pom
com.google.cloud
libraries-bom
- 26.43.0
+ 26.44.0
import
pom
@@ -62,7 +62,7 @@
org.postgresql
postgresql
- 42.7.3
+ 42.7.4
@@ -97,7 +97,7 @@
com.spotify.fmt
fmt-maven-plugin
- 2.23
+ 2.24
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ConcurrentTransactionOnEmulatorTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ConcurrentTransactionOnEmulatorTest.java
index 887412048..74a005038 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/ConcurrentTransactionOnEmulatorTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/ConcurrentTransactionOnEmulatorTest.java
@@ -45,7 +45,13 @@ public class ConcurrentTransactionOnEmulatorTest {
@BeforeClass
public static void startEmulator() {
- assumeTrue(DockerClientFactory.instance().isDockerAvailable());
+ boolean dockerAvailable = false;
+ try {
+ dockerAvailable = DockerClientFactory.instance().isDockerAvailable();
+ } catch (Exception ignore) {
+ // Ignore, and just skip the test.
+ }
+ assumeTrue(dockerAvailable);
emulator =
new GenericContainer<>(
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/SingleJarTestApplication.java b/src/test/java/com/google/cloud/spanner/jdbc/SingleJarTestApplication.java
new file mode 100644
index 000000000..e9cfba1b2
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/SingleJarTestApplication.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 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 java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+
+/**
+ * Simple Java application that is used to verify the working of the single-jar-with-dependencies.
+ */
+public class SingleJarTestApplication {
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 3) {
+ throw new IllegalArgumentException("expected 3 arguments");
+ }
+ String project = args[0];
+ String instance = args[1];
+ String database = args[2];
+ String extraOptions = "";
+ String host = "";
+ if (System.getenv("SPANNER_EMULATOR_HOST") != null) {
+ extraOptions = "?autoConfigEmulator=true";
+ host = "//" + System.getenv("SPANNER_EMULATOR_HOST");
+ }
+
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:%s/projects/%s/instances/%s/databases/%s%s",
+ host, project, instance, database, extraOptions))) {
+ try (ResultSet resultSet =
+ connection.createStatement().executeQuery("select 'Hello World from Real Spanner!'")) {
+ while (resultSet.next()) {
+ System.out.println(resultSet.getString(1));
+ }
+ }
+ }
+ }
+}
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 903df91db..efc3934d3 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,9 +21,13 @@
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;
+import com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.ParallelIntegrationTest;
+import com.google.cloud.spanner.testing.EmulatorSpannerHelper;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
@@ -31,7 +35,8 @@
import java.sql.Types;
import java.util.Arrays;
import java.util.List;
-import org.junit.Before;
+import java.util.stream.Collectors;
+import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@@ -53,11 +58,46 @@ public class ITJdbcDatabaseMetaDataTest extends ITAbstractJdbcTest {
@ClassRule public static JdbcIntegrationTestEnv env = new JdbcIntegrationTestEnv();
- private Database database;
-
- @Before
- public void setup() {
- database = env.getOrCreateDatabase(getDialect(), getMusicTablesDdl(getDialect()));
+ private static Database database;
+
+ @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));
+
+ // Create the same tables in an additional 'test' schema.
+ DatabaseAdminClient client = env.getTestHelper().getClient().getDatabaseAdminClient();
+ List tables =
+ getMusicTablesDdl(Dialect.GOOGLE_STANDARD_SQL).stream()
+ .map(statement -> statement.replace("CREATE TABLE ", "CREATE TABLE test."))
+ .map(statement -> statement.replace("CREATE INDEX ", "CREATE INDEX test."))
+ .map(
+ statement -> statement.replace("CREATE UNIQUE INDEX ", "CREATE UNIQUE INDEX test."))
+ .map(statement -> statement.replace("CREATE VIEW ", "CREATE VIEW test."))
+ .map(statement -> statement.replace("FROM ", "FROM test."))
+ .map(
+ statement ->
+ statement.replace("INTERLEAVE IN PARENT ", "INTERLEAVE IN PARENT test."))
+ .map(statement -> statement.replace("INTERLEAVE IN ", "INTERLEAVE IN test."))
+ .map(
+ statement -> statement.replace("INTERLEAVE IN test.PARENT", "INTERLEAVE IN PARENT"))
+ .map(statement -> statement.replace(" ON ", " ON test."))
+ .map(statement -> statement.replace(" ON test.DELETE", " ON DELETE"))
+ .map(statement -> statement.replace(" REFERENCES ", " REFERENCES test."))
+ .collect(Collectors.toList());
+ tables.add(0, "create schema test");
+ client
+ .updateDatabaseDdl(
+ database.getId().getInstanceId().getInstance(),
+ database.getId().getDatabase(),
+ tables,
+ null)
+ .get();
}
private static final class Column {
@@ -165,65 +205,65 @@ private Column(
@Test
public void testGetColumns() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getColumns(DEFAULT_CATALOG, DEFAULT_SCHEMA, TABLE_WITH_ALL_COLS, null)) {
- int pos = 1;
- for (Column col : EXPECTED_COLUMNS) {
- assertTrue(rs.next());
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals(TABLE_WITH_ALL_COLS, rs.getString("TABLE_NAME"));
- assertEquals(col.name, rs.getString("COLUMN_NAME"));
- assertEquals(col.type, rs.getInt("DATA_TYPE"));
- assertEquals(col.typeName, rs.getString("TYPE_NAME"));
- if (col.colSize == null) {
- assertEquals(0, rs.getInt("COLUMN_SIZE"));
- assertTrue(rs.wasNull());
- } else {
- assertEquals(col.colSize.intValue(), rs.getInt("COLUMN_SIZE"));
- }
- rs.getObject("BUFFER_LENGTH"); // just assert that it exists
- if (col.decimalDigits == null) {
- assertEquals(0, rs.getInt("DECIMAL_DIGITS"));
- assertTrue(rs.wasNull());
- } else {
- assertEquals(col.decimalDigits.intValue(), rs.getInt("DECIMAL_DIGITS"));
- }
- if (col.radix == null) {
- assertEquals(0, rs.getInt("NUM_PREC_RADIX"));
- assertTrue(rs.wasNull());
- } else {
- assertEquals(col.radix.intValue(), rs.getInt("NUM_PREC_RADIX"));
- }
- assertEquals(
- col.nullable ? DatabaseMetaData.columnNullable : DatabaseMetaData.columnNoNulls,
- rs.getInt("NULLABLE"));
- assertNull(rs.getString("REMARKS"));
- assertNull(rs.getString("COLUMN_DEF"));
- assertEquals(0, rs.getInt("SQL_DATA_TYPE"));
- assertEquals(0, rs.getInt("SQL_DATETIME_SUB"));
- if (col.charOctetLength == null) {
- assertEquals(0, rs.getInt("CHAR_OCTET_LENGTH"));
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getColumns(DEFAULT_CATALOG, schema, TABLE_WITH_ALL_COLS, null)) {
+ int ordinalPosition = 0;
+ for (Column col : EXPECTED_COLUMNS) {
+ assertTrue(rs.next());
+ assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals(TABLE_WITH_ALL_COLS, rs.getString("TABLE_NAME"));
+ assertEquals(col.name, rs.getString("COLUMN_NAME"));
+ assertEquals(col.type, rs.getInt("DATA_TYPE"));
+ assertEquals(col.typeName, rs.getString("TYPE_NAME"));
+ if (col.colSize == null) {
+ assertEquals(0, rs.getInt("COLUMN_SIZE"));
+ assertTrue(rs.wasNull());
+ } else {
+ assertEquals(col.colSize.intValue(), rs.getInt("COLUMN_SIZE"));
+ }
+ rs.getObject("BUFFER_LENGTH"); // just assert that it exists
+ if (col.decimalDigits == null) {
+ assertEquals(0, rs.getInt("DECIMAL_DIGITS"));
+ assertTrue(rs.wasNull());
+ } else {
+ assertEquals(col.decimalDigits.intValue(), rs.getInt("DECIMAL_DIGITS"));
+ }
+ if (col.radix == null) {
+ assertEquals(0, rs.getInt("NUM_PREC_RADIX"));
+ assertTrue(rs.wasNull());
+ } else {
+ assertEquals(col.radix.intValue(), rs.getInt("NUM_PREC_RADIX"));
+ }
+ assertEquals(
+ col.nullable ? DatabaseMetaData.columnNullable : DatabaseMetaData.columnNoNulls,
+ rs.getInt("NULLABLE"));
+ assertNull(rs.getString("REMARKS"));
+ assertNull(rs.getString("COLUMN_DEF"));
+ assertEquals(0, rs.getInt("SQL_DATA_TYPE"));
+ assertEquals(0, rs.getInt("SQL_DATETIME_SUB"));
+ if (col.charOctetLength == null) {
+ assertEquals(0, rs.getInt("CHAR_OCTET_LENGTH"));
+ assertTrue(rs.wasNull());
+ } else {
+ assertEquals(col.charOctetLength.intValue(), rs.getInt("CHAR_OCTET_LENGTH"));
+ }
+ assertEquals(++ordinalPosition, rs.getInt("ORDINAL_POSITION"));
+ assertEquals(col.nullable ? "YES" : "NO", rs.getString("IS_NULLABLE"));
+ assertNull(rs.getString("SCOPE_CATALOG"));
+ assertNull(rs.getString("SCOPE_SCHEMA"));
+ assertNull(rs.getString("SCOPE_TABLE"));
+ assertEquals(0, rs.getShort("SOURCE_DATA_TYPE"));
assertTrue(rs.wasNull());
- } else {
- assertEquals(col.charOctetLength.intValue(), rs.getInt("CHAR_OCTET_LENGTH"));
+ assertEquals("NO", rs.getString("IS_AUTOINCREMENT"));
+ assertEquals(col.computed ? "YES" : "NO", rs.getString("IS_GENERATEDCOLUMN"));
+ assertEquals(24, rs.getMetaData().getColumnCount());
}
- assertEquals(pos, rs.getInt("ORDINAL_POSITION"));
- assertEquals(col.nullable ? "YES" : "NO", rs.getString("IS_NULLABLE"));
- assertNull(rs.getString("SCOPE_CATALOG"));
- assertNull(rs.getString("SCOPE_SCHEMA"));
- assertNull(rs.getString("SCOPE_TABLE"));
- assertEquals(0, rs.getShort("SOURCE_DATA_TYPE"));
- assertTrue(rs.wasNull());
- assertEquals("NO", rs.getString("IS_AUTOINCREMENT"));
- assertEquals(col.computed ? "YES" : "NO", rs.getString("IS_GENERATEDCOLUMN"));
- assertEquals(24, rs.getMetaData().getColumnCount());
-
- pos++;
+ assertFalse(rs.next());
}
- assertFalse(rs.next());
}
}
}
@@ -231,181 +271,173 @@ public void testGetColumns() throws SQLException {
@Test
public void testGetCrossReferences() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- SINGERS_TABLE,
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- ALBUMS_TABLE)) {
- assertTrue(rs.next());
- assertEquals("", rs.getString("PKTABLE_CAT"));
- assertEquals("", rs.getString("PKTABLE_SCHEM"));
- assertEquals("Singers", rs.getString("PKTABLE_NAME"));
- assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
- assertEquals("", rs.getString("FKTABLE_CAT"));
- assertEquals("", rs.getString("FKTABLE_SCHEM"));
- assertEquals("Albums", rs.getString("FKTABLE_NAME"));
- assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
- assertEquals(1, rs.getShort("KEY_SEQ"));
- assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
- assertEquals(DatabaseMetaData.importedKeyCascade, rs.getShort("DELETE_RULE"));
- assertNull(rs.getString("FK_NAME"));
- assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
- assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- ALBUMS_TABLE,
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- SONGS_TABLE)) {
- assertTrue(rs.next());
- assertEquals("", rs.getString("PKTABLE_CAT"));
- assertEquals("", rs.getString("PKTABLE_SCHEM"));
- assertEquals("Albums", rs.getString("PKTABLE_NAME"));
- assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
- assertEquals("", rs.getString("FKTABLE_CAT"));
- assertEquals("", rs.getString("FKTABLE_SCHEM"));
- assertEquals("Songs", rs.getString("FKTABLE_NAME"));
- assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
- assertEquals(1, rs.getShort("KEY_SEQ"));
- assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
- assertEquals(DatabaseMetaData.importedKeyCascade, rs.getShort("DELETE_RULE"));
- assertNull(rs.getString("FK_NAME"));
- assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
- assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
-
- assertTrue(rs.next());
- assertEquals("", rs.getString("PKTABLE_CAT"));
- assertEquals("", rs.getString("PKTABLE_SCHEM"));
- assertEquals("Albums", rs.getString("PKTABLE_NAME"));
- assertEquals("AlbumId", rs.getString("PKCOLUMN_NAME"));
- assertEquals("", rs.getString("FKTABLE_CAT"));
- assertEquals("", rs.getString("FKTABLE_SCHEM"));
- assertEquals("Songs", rs.getString("FKTABLE_NAME"));
- assertEquals("AlbumId", rs.getString("FKCOLUMN_NAME"));
- assertEquals(2, rs.getShort("KEY_SEQ"));
- assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
- assertEquals(DatabaseMetaData.importedKeyCascade, rs.getShort("DELETE_RULE"));
- assertNull(rs.getString("FK_NAME"));
- assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
- assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- }
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ DEFAULT_CATALOG,
+ schema,
+ SINGERS_TABLE,
+ DEFAULT_CATALOG,
+ schema,
+ ALBUMS_TABLE)) {
+ assertTrue(rs.next());
+ assertEquals("", rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("Singers", rs.getString("PKTABLE_NAME"));
+ assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
+ assertEquals("", rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("Albums", rs.getString("FKTABLE_NAME"));
+ assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
+ assertEquals(1, rs.getShort("KEY_SEQ"));
+ assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
+ assertEquals(DatabaseMetaData.importedKeyCascade, rs.getShort("DELETE_RULE"));
+ assertNull(rs.getString("FK_NAME"));
+ assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
+ assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ }
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ DEFAULT_CATALOG, schema, ALBUMS_TABLE, DEFAULT_CATALOG, schema, SONGS_TABLE)) {
+ assertTrue(rs.next());
+ assertEquals("", rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("Albums", rs.getString("PKTABLE_NAME"));
+ assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
+ assertEquals("", rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("Songs", rs.getString("FKTABLE_NAME"));
+ assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
+ assertEquals(1, rs.getShort("KEY_SEQ"));
+ assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
+ assertEquals(DatabaseMetaData.importedKeyCascade, rs.getShort("DELETE_RULE"));
+ assertNull(rs.getString("FK_NAME"));
+ assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
+ assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- SINGERS_TABLE,
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- CONCERTS_TABLE)) {
- assertTrue(rs.next());
- assertEquals("", rs.getString("PKTABLE_CAT"));
- assertEquals("", rs.getString("PKTABLE_SCHEM"));
- assertEquals("Singers", rs.getString("PKTABLE_NAME"));
- assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
- assertEquals("", rs.getString("FKTABLE_CAT"));
- assertEquals("", rs.getString("FKTABLE_SCHEM"));
- assertEquals("Concerts", rs.getString("FKTABLE_NAME"));
- assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
- 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"));
- assertEquals("PK_Singers", rs.getString("PK_NAME"));
- assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- assertFalse(rs.next());
- }
+ assertTrue(rs.next());
+ assertEquals("", rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("Albums", rs.getString("PKTABLE_NAME"));
+ assertEquals("AlbumId", rs.getString("PKCOLUMN_NAME"));
+ assertEquals("", rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("Songs", rs.getString("FKTABLE_NAME"));
+ assertEquals("AlbumId", rs.getString("FKCOLUMN_NAME"));
+ assertEquals(2, rs.getShort("KEY_SEQ"));
+ assertEquals(DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
+ assertEquals(DatabaseMetaData.importedKeyCascade, rs.getShort("DELETE_RULE"));
+ assertNull(rs.getString("FK_NAME"));
+ assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
+ assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- TABLE_WITH_ALL_COLS,
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- TABLE_WITH_REF)) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ DEFAULT_CATALOG,
+ schema,
+ SINGERS_TABLE,
+ DEFAULT_CATALOG,
+ schema,
+ CONCERTS_TABLE)) {
+ assertTrue(rs.next());
+ assertEquals("", rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("Singers", rs.getString("PKTABLE_NAME"));
+ assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
+ assertEquals("", rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("Concerts", rs.getString("FKTABLE_NAME"));
+ assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
+ 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"));
+ assertEquals("PK_Singers", rs.getString("PK_NAME"));
+ assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ assertFalse(rs.next());
+ }
- assertTrue(rs.next());
- assertEquals("", rs.getString("PKTABLE_CAT"));
- assertEquals("", rs.getString("PKTABLE_SCHEM"));
- assertEquals(TABLE_WITH_ALL_COLS, rs.getString("PKTABLE_NAME"));
- assertEquals("ColFloat64", rs.getString("PKCOLUMN_NAME"));
- assertEquals("", rs.getString("FKTABLE_CAT"));
- assertEquals("", rs.getString("FKTABLE_SCHEM"));
- assertEquals(TABLE_WITH_REF, rs.getString("FKTABLE_NAME"));
- assertEquals("RefFloat", rs.getString("FKCOLUMN_NAME"));
- 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"));
- assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ DEFAULT_CATALOG,
+ schema,
+ TABLE_WITH_ALL_COLS,
+ DEFAULT_CATALOG,
+ schema,
+ TABLE_WITH_REF)) {
- assertTrue(rs.next());
- assertEquals("", rs.getString("PKTABLE_CAT"));
- assertEquals("", rs.getString("PKTABLE_SCHEM"));
- assertEquals(TABLE_WITH_ALL_COLS, rs.getString("PKTABLE_NAME"));
- assertEquals("ColString", rs.getString("PKCOLUMN_NAME"));
- assertEquals("", rs.getString("FKTABLE_CAT"));
- assertEquals("", rs.getString("FKTABLE_SCHEM"));
- assertEquals(TABLE_WITH_REF, rs.getString("FKTABLE_NAME"));
- assertEquals("RefString", rs.getString("FKCOLUMN_NAME"));
- 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"));
- assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ assertTrue(rs.next());
+ assertEquals("", rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(TABLE_WITH_ALL_COLS, rs.getString("PKTABLE_NAME"));
+ assertEquals("ColFloat64", rs.getString("PKCOLUMN_NAME"));
+ assertEquals("", rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(TABLE_WITH_REF, rs.getString("FKTABLE_NAME"));
+ assertEquals("RefFloat", rs.getString("FKCOLUMN_NAME"));
+ 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"));
+ assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- assertTrue(rs.next());
- assertEquals("", rs.getString("PKTABLE_CAT"));
- assertEquals("", rs.getString("PKTABLE_SCHEM"));
- assertEquals(TABLE_WITH_ALL_COLS, rs.getString("PKTABLE_NAME"));
- assertEquals("ColDate", rs.getString("PKCOLUMN_NAME"));
- assertEquals("", rs.getString("FKTABLE_CAT"));
- assertEquals("", rs.getString("FKTABLE_SCHEM"));
- assertEquals(TABLE_WITH_REF, rs.getString("FKTABLE_NAME"));
- assertEquals("RefDate", rs.getString("FKCOLUMN_NAME"));
- 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"));
- assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ assertTrue(rs.next());
+ assertEquals("", rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(TABLE_WITH_ALL_COLS, rs.getString("PKTABLE_NAME"));
+ assertEquals("ColString", rs.getString("PKCOLUMN_NAME"));
+ assertEquals("", rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(TABLE_WITH_REF, rs.getString("FKTABLE_NAME"));
+ assertEquals("RefString", rs.getString("FKCOLUMN_NAME"));
+ 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"));
+ assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- assertFalse(rs.next());
- }
- // try getting self-references
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- ALBUMS_TABLE,
- DEFAULT_CATALOG,
- DEFAULT_SCHEMA,
- ALBUMS_TABLE)) {
- assertFalse(rs.next());
- }
- // try getting all cross-references in the database
- try (ResultSet rs =
- connection.getMetaData().getCrossReference(null, null, null, null, null, null)) {
- for (int i = 0; i < 7; i++) {
assertTrue(rs.next());
+ assertEquals("", rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(TABLE_WITH_ALL_COLS, rs.getString("PKTABLE_NAME"));
+ assertEquals("ColDate", rs.getString("PKCOLUMN_NAME"));
+ assertEquals("", rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(TABLE_WITH_REF, rs.getString("FKTABLE_NAME"));
+ assertEquals("RefDate", rs.getString("FKCOLUMN_NAME"));
+ 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"));
+ assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+
+ assertFalse(rs.next());
+ }
+ // try getting self-references
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ DEFAULT_CATALOG, schema, ALBUMS_TABLE, DEFAULT_CATALOG, schema, ALBUMS_TABLE)) {
+ assertFalse(rs.next());
+ }
+ // try getting all cross-references in the database
+ try (ResultSet rs =
+ connection.getMetaData().getCrossReference(null, null, null, null, null, null)) {
+ for (int i = 0; i < 14; i++) {
+ assertTrue(rs.next());
+ }
+ assertFalse(rs.next());
}
- assertFalse(rs.next());
}
}
}
@@ -480,39 +512,39 @@ private IndexInfo(
@Test
public void testGetIndexInfo() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getIndexInfo(DEFAULT_CATALOG, DEFAULT_SCHEMA, null, false, false)) {
-
- for (IndexInfo index : EXPECTED_INDICES) {
- assertTrue(rs.next());
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals(index.tableName, rs.getString("TABLE_NAME"));
- assertEquals(index.nonUnique, rs.getBoolean("NON_UNIQUE"));
- assertEquals(DEFAULT_CATALOG, rs.getString("INDEX_QUALIFIER"));
- // Foreign key index names are automatically generated.
- if (!"FOREIGN_KEY".equals(index.indexName) && !"GENERATED".equals(index.indexName)) {
- assertEquals(index.indexName, rs.getString("INDEX_NAME"));
- }
- if (index.indexName.equals("PRIMARY_KEY")) {
- assertEquals(DatabaseMetaData.tableIndexClustered, rs.getShort("TYPE"));
- } else {
- assertEquals(DatabaseMetaData.tableIndexHashed, rs.getShort("TYPE"));
- }
- assertEquals(index.ordinalPosition, rs.getShort("ORDINAL_POSITION"));
- if (index.ordinalPosition == 0) {
- assertTrue(rs.wasNull());
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection.getMetaData().getIndexInfo(DEFAULT_CATALOG, schema, null, false, false)) {
+
+ for (IndexInfo index : EXPECTED_INDICES) {
+ assertTrue(rs.next());
+ assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals(index.tableName, rs.getString("TABLE_NAME"));
+ assertEquals(index.nonUnique, rs.getBoolean("NON_UNIQUE"));
+ assertEquals(DEFAULT_CATALOG, rs.getString("INDEX_QUALIFIER"));
+ // Foreign key index names are automatically generated.
+ if (!"FOREIGN_KEY".equals(index.indexName) && !"GENERATED".equals(index.indexName)) {
+ assertEquals(index.indexName, rs.getString("INDEX_NAME"));
+ }
+ if (index.indexName.equals("PRIMARY_KEY")) {
+ assertEquals(DatabaseMetaData.tableIndexClustered, rs.getShort("TYPE"));
+ } else {
+ assertEquals(DatabaseMetaData.tableIndexHashed, rs.getShort("TYPE"));
+ }
+ assertEquals(index.ordinalPosition, rs.getShort("ORDINAL_POSITION"));
+ if (index.ordinalPosition == 0) {
+ assertTrue(rs.wasNull());
+ }
+ assertEquals(index.columnName, rs.getString("COLUMN_NAME"));
+ assertEquals(index.ascDesc, rs.getString("ASC_OR_DESC"));
+ assertEquals(-1, rs.getInt("CARDINALITY"));
+ assertEquals(-1, rs.getInt("PAGES"));
+ assertNull(rs.getString("FILTER_CONDITION"));
}
- assertEquals(index.columnName, rs.getString("COLUMN_NAME"));
- assertEquals(index.ascDesc, rs.getString("ASC_OR_DESC"));
- assertEquals(-1, rs.getInt("CARDINALITY"));
- assertEquals(-1, rs.getInt("PAGES"));
- assertNull(rs.getString("FILTER_CONDITION"));
+ // all indices found
+ assertFalse(rs.next());
}
- // all indices found
- assertFalse(rs.next());
}
}
}
@@ -520,15 +552,15 @@ public void testGetIndexInfo() throws SQLException {
@Test
public void testGetExportedKeys() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getExportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, SINGERS_TABLE)) {
- assertExportedKeysSingers(rs);
- }
- try (ResultSet rs =
- connection.getMetaData().getExportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, ALBUMS_TABLE)) {
- assertKeysAlbumsSongs(rs);
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection.getMetaData().getExportedKeys(DEFAULT_CATALOG, schema, SINGERS_TABLE)) {
+ assertExportedKeysSingers(schema, rs);
+ }
+ try (ResultSet rs =
+ connection.getMetaData().getExportedKeys(DEFAULT_CATALOG, schema, ALBUMS_TABLE)) {
+ assertKeysAlbumsSongs(schema, rs);
+ }
}
}
}
@@ -536,31 +568,27 @@ public void testGetExportedKeys() throws SQLException {
@Test
public void testGetImportedKeys() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, SINGERS_TABLE)) {
- assertImportedKeysSingers(rs);
- }
- try (ResultSet rs =
- connection.getMetaData().getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, ALBUMS_TABLE)) {
- assertImportedKeysAlbums(rs);
- }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, CONCERTS_TABLE)) {
- assertImportedKeysConcerts(rs);
- }
- try (ResultSet rs =
- connection.getMetaData().getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, SONGS_TABLE)) {
- assertKeysAlbumsSongs(rs);
- }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, TABLE_WITH_REF)) {
- assertImportedKeysTableWithRef(rs);
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection.getMetaData().getImportedKeys(DEFAULT_CATALOG, schema, SINGERS_TABLE)) {
+ assertImportedKeysSingers(rs);
+ }
+ try (ResultSet rs =
+ connection.getMetaData().getImportedKeys(DEFAULT_CATALOG, schema, ALBUMS_TABLE)) {
+ assertImportedKeysAlbums(schema, rs);
+ }
+ try (ResultSet rs =
+ connection.getMetaData().getImportedKeys(DEFAULT_CATALOG, schema, CONCERTS_TABLE)) {
+ assertImportedKeysConcerts(schema, rs);
+ }
+ try (ResultSet rs =
+ connection.getMetaData().getImportedKeys(DEFAULT_CATALOG, schema, SONGS_TABLE)) {
+ assertKeysAlbumsSongs(schema, rs);
+ }
+ try (ResultSet rs =
+ connection.getMetaData().getImportedKeys(DEFAULT_CATALOG, schema, TABLE_WITH_REF)) {
+ assertImportedKeysTableWithRef(schema, rs);
+ }
}
}
}
@@ -569,14 +597,14 @@ private void assertImportedKeysSingers(ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertImportedKeysTableWithRef(ResultSet rs) throws SQLException {
+ private void assertImportedKeysTableWithRef(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals(TABLE_WITH_ALL_COLS, rs.getString("PKTABLE_NAME"));
assertEquals("ColFloat64", rs.getString("PKCOLUMN_NAME"));
assertEquals(DEFAULT_CATALOG, rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals(TABLE_WITH_REF, rs.getString("FKTABLE_NAME"));
assertEquals("RefFloat", rs.getString("FKCOLUMN_NAME"));
assertEquals(1, rs.getShort("KEY_SEQ"));
@@ -588,11 +616,11 @@ private void assertImportedKeysTableWithRef(ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals(TABLE_WITH_ALL_COLS, rs.getString("PKTABLE_NAME"));
assertEquals("ColString", rs.getString("PKCOLUMN_NAME"));
assertEquals(DEFAULT_CATALOG, rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals(TABLE_WITH_REF, rs.getString("FKTABLE_NAME"));
assertEquals("RefString", rs.getString("FKCOLUMN_NAME"));
assertEquals(2, rs.getShort("KEY_SEQ"));
@@ -604,11 +632,11 @@ private void assertImportedKeysTableWithRef(ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals(TABLE_WITH_ALL_COLS, rs.getString("PKTABLE_NAME"));
assertEquals("ColDate", rs.getString("PKCOLUMN_NAME"));
assertEquals(DEFAULT_CATALOG, rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals(TABLE_WITH_REF, rs.getString("FKTABLE_NAME"));
assertEquals("RefDate", rs.getString("FKCOLUMN_NAME"));
assertEquals(3, rs.getShort("KEY_SEQ"));
@@ -621,14 +649,14 @@ private void assertImportedKeysTableWithRef(ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertImportedKeysAlbums(ResultSet rs) throws SQLException {
+ private void assertImportedKeysAlbums(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals(SINGERS_TABLE, rs.getString("PKTABLE_NAME"));
assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
assertEquals(DEFAULT_CATALOG, rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals(ALBUMS_TABLE, rs.getString("FKTABLE_NAME"));
assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
assertEquals(1, rs.getShort("KEY_SEQ"));
@@ -641,14 +669,14 @@ private void assertImportedKeysAlbums(ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertImportedKeysConcerts(ResultSet rs) throws SQLException {
+ private void assertImportedKeysConcerts(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals(SINGERS_TABLE, rs.getString("PKTABLE_NAME"));
assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
assertEquals(DEFAULT_CATALOG, rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals(CONCERTS_TABLE, rs.getString("FKTABLE_NAME"));
assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
assertEquals(1, rs.getShort("KEY_SEQ"));
@@ -661,14 +689,14 @@ private void assertImportedKeysConcerts(ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertExportedKeysSingers(ResultSet rs) throws SQLException {
+ private void assertExportedKeysSingers(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals(SINGERS_TABLE, rs.getString("PKTABLE_NAME"));
assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
assertEquals(DEFAULT_CATALOG, rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals(ALBUMS_TABLE, rs.getString("FKTABLE_NAME"));
assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
assertEquals(1, rs.getShort("KEY_SEQ"));
@@ -680,11 +708,11 @@ private void assertExportedKeysSingers(ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals(SINGERS_TABLE, rs.getString("PKTABLE_NAME"));
assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
assertEquals(DEFAULT_CATALOG, rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals(CONCERTS_TABLE, rs.getString("FKTABLE_NAME"));
assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
assertEquals(1, rs.getShort("KEY_SEQ"));
@@ -697,14 +725,14 @@ private void assertExportedKeysSingers(ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertKeysAlbumsSongs(ResultSet rs) throws SQLException {
+ private void assertKeysAlbumsSongs(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals(ALBUMS_TABLE, rs.getString("PKTABLE_NAME"));
assertEquals("SingerId", rs.getString("PKCOLUMN_NAME"));
assertEquals(DEFAULT_CATALOG, rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals(SONGS_TABLE, rs.getString("FKTABLE_NAME"));
assertEquals("SingerId", rs.getString("FKCOLUMN_NAME"));
assertEquals(1, rs.getShort("KEY_SEQ"));
@@ -716,11 +744,11 @@ private void assertKeysAlbumsSongs(ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(DEFAULT_CATALOG, rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals(ALBUMS_TABLE, rs.getString("PKTABLE_NAME"));
assertEquals("AlbumId", rs.getString("PKCOLUMN_NAME"));
assertEquals(DEFAULT_CATALOG, rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals(SONGS_TABLE, rs.getString("FKTABLE_NAME"));
assertEquals("AlbumId", rs.getString("FKCOLUMN_NAME"));
assertEquals(2, rs.getShort("KEY_SEQ"));
@@ -735,48 +763,54 @@ private void assertKeysAlbumsSongs(ResultSet rs) throws SQLException {
@Test
public void testGetPrimaryKeys() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection.getMetaData().getPrimaryKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, SINGERS_TABLE)) {
- assertTrue(rs.next());
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals(SINGERS_TABLE, rs.getString("TABLE_NAME"));
- assertEquals("SingerId", rs.getString("COLUMN_NAME"));
- assertEquals(1, rs.getInt("KEY_SEQ"));
- assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
- assertFalse(rs.next());
- }
- try (ResultSet rs =
- connection.getMetaData().getPrimaryKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, ALBUMS_TABLE)) {
- assertTrue(rs.next());
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals(ALBUMS_TABLE, rs.getString("TABLE_NAME"));
- assertEquals("SingerId", rs.getString("COLUMN_NAME"));
- assertEquals(1, rs.getInt("KEY_SEQ"));
- assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
- assertTrue(rs.next());
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals(ALBUMS_TABLE, rs.getString("TABLE_NAME"));
- assertEquals("AlbumId", rs.getString("COLUMN_NAME"));
- assertEquals(2, rs.getInt("KEY_SEQ"));
- assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
- assertFalse(rs.next());
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection.getMetaData().getPrimaryKeys(DEFAULT_CATALOG, schema, SINGERS_TABLE)) {
+ assertTrue(rs.next());
+ assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals(SINGERS_TABLE, rs.getString("TABLE_NAME"));
+ assertEquals("SingerId", rs.getString("COLUMN_NAME"));
+ assertEquals(1, rs.getInt("KEY_SEQ"));
+ assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
+ assertFalse(rs.next());
+ }
+ try (ResultSet rs =
+ connection.getMetaData().getPrimaryKeys(DEFAULT_CATALOG, schema, ALBUMS_TABLE)) {
+ assertTrue(rs.next());
+ assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals(ALBUMS_TABLE, rs.getString("TABLE_NAME"));
+ assertEquals("SingerId", rs.getString("COLUMN_NAME"));
+ assertEquals(1, rs.getInt("KEY_SEQ"));
+ assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
+ assertTrue(rs.next());
+ assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals(ALBUMS_TABLE, rs.getString("TABLE_NAME"));
+ assertEquals("AlbumId", rs.getString("COLUMN_NAME"));
+ assertEquals(2, rs.getInt("KEY_SEQ"));
+ assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
+ assertFalse(rs.next());
+ }
}
}
}
@Test
public void testGetViews() throws SQLException {
- // assumeFalse("Emulator does not yet support views", EmulatorSpannerHelper.isUsingEmulator());
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs = connection.getMetaData().getTables("", "", null, new String[] {"VIEW"})) {
- assertTrue(rs.next());
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
- assertEquals("SingersView", rs.getString("TABLE_NAME"));
- assertFalse(rs.next());
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getTables(DEFAULT_CATALOG, schema, null, new String[] {"VIEW"})) {
+ assertTrue(rs.next());
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
+ assertEquals("SingersView", rs.getString("TABLE_NAME"));
+ assertFalse(rs.next());
+ }
}
}
}
@@ -785,17 +819,24 @@ public void testGetViews() throws SQLException {
public void testGetSchemas() throws SQLException {
try (Connection connection = createConnection(env, database)) {
assertEquals("", connection.getSchema());
- try (ResultSet rs = connection.getMetaData().getSchemas()) {
- assertTrue(rs.next());
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CATALOG"));
- assertTrue(rs.next());
- assertEquals("INFORMATION_SCHEMA", rs.getString("TABLE_SCHEM"));
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CATALOG"));
- assertTrue(rs.next());
- assertEquals("SPANNER_SYS", rs.getString("TABLE_SCHEM"));
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CATALOG"));
- assertFalse(rs.next());
+ try (ResultSet schemas = connection.getMetaData().getSchemas()) {
+ assertTrue(schemas.next());
+ assertEquals(DEFAULT_SCHEMA, schemas.getString("TABLE_SCHEM"));
+ assertEquals(DEFAULT_CATALOG, schemas.getString("TABLE_CATALOG"));
+
+ assertTrue(schemas.next());
+ assertEquals("INFORMATION_SCHEMA", schemas.getString("TABLE_SCHEM"));
+ assertEquals(DEFAULT_CATALOG, schemas.getString("TABLE_CATALOG"));
+
+ assertTrue(schemas.next());
+ assertEquals("SPANNER_SYS", schemas.getString("TABLE_SCHEM"));
+ assertEquals(DEFAULT_CATALOG, schemas.getString("TABLE_CATALOG"));
+
+ assertTrue(schemas.next());
+ assertEquals("test", schemas.getString("TABLE_SCHEM"));
+ assertEquals(DEFAULT_CATALOG, schemas.getString("TABLE_CATALOG"));
+
+ assertFalse(schemas.next());
}
}
}
@@ -841,22 +882,27 @@ private Table(String name, String type) {
@Test
public void testGetTables() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection.getMetaData().getTables(DEFAULT_CATALOG, DEFAULT_SCHEMA, null, null)) {
- for (Table table : EXPECTED_TABLES) {
- assertTrue(rs.next());
- assertEquals(DEFAULT_CATALOG, rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals(table.name, rs.getString("TABLE_NAME"));
- assertEquals(table.type, rs.getString("TABLE_TYPE"));
- assertNull(rs.getString("REMARKS"));
- assertNull(rs.getString("TYPE_CAT"));
- assertNull(rs.getString("TYPE_SCHEM"));
- assertNull(rs.getString("TYPE_NAME"));
- assertNull(rs.getString("SELF_REFERENCING_COL_NAME"));
- assertNull(rs.getString("REF_GENERATION"));
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ 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"));
+ assertEquals(table.name, rs.getString("TABLE_NAME"));
+ assertEquals(table.type, rs.getString("TABLE_TYPE"));
+ assertNull(rs.getString("REMARKS"));
+ assertNull(rs.getString("TYPE_CAT"));
+ assertNull(rs.getString("TYPE_SCHEM"));
+ assertNull(rs.getString("TYPE_NAME"));
+ assertNull(rs.getString("SELF_REFERENCING_COL_NAME"));
+ assertNull(rs.getString("REF_GENERATION"));
+ }
+ assertFalse(rs.next());
}
- assertFalse(rs.next());
}
}
}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcPgDatabaseMetaDataTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcPgDatabaseMetaDataTest.java
index 1b96e9569..033ff27e4 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcPgDatabaseMetaDataTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcPgDatabaseMetaDataTest.java
@@ -21,10 +21,13 @@
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;
import com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.ParallelIntegrationTest;
+import com.google.cloud.spanner.testing.EmulatorSpannerHelper;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
@@ -32,9 +35,9 @@
import java.sql.Types;
import java.util.Arrays;
import java.util.List;
-import org.junit.Before;
+import java.util.stream.Collectors;
+import org.junit.BeforeClass;
import org.junit.ClassRule;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -54,11 +57,41 @@ public class ITJdbcPgDatabaseMetaDataTest extends ITAbstractJdbcTest {
private static final String TABLE_WITH_ALL_COLS = "TableWithAllColumnTypes";
private static final String TABLE_WITH_REF = "TableWithRef";
- private Database database;
-
- @Before
- public void setup() {
- database = env.getOrCreateDatabase(getDialect(), getMusicTablesDdl(getDialect()));
+ private static Database database;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ assumeFalse(
+ "PostgreSQL dialect is not yet supported on the emulator",
+ EmulatorSpannerHelper.isUsingEmulator());
+
+ database = env.getOrCreateDatabase(Dialect.POSTGRESQL, getMusicTablesDdl(Dialect.POSTGRESQL));
+
+ // Create the same tables in an additional 'test' schema.
+ DatabaseAdminClient client = env.getTestHelper().getClient().getDatabaseAdminClient();
+ List tables =
+ getMusicTablesDdl(Dialect.POSTGRESQL).stream()
+ .map(statement -> statement.replace("CREATE TABLE ", "CREATE TABLE test."))
+ .map(statement -> statement.replace("CREATE VIEW ", "CREATE VIEW test."))
+ .map(statement -> statement.replace("FROM ", "FROM test."))
+ .map(
+ statement ->
+ statement.replace("INTERLEAVE IN PARENT ", "INTERLEAVE IN PARENT test."))
+ .map(statement -> statement.replace("INTERLEAVE IN ", "INTERLEAVE IN test."))
+ .map(
+ statement -> statement.replace("INTERLEAVE IN test.PARENT", "INTERLEAVE IN PARENT"))
+ .map(statement -> statement.replace(" ON ", " ON test."))
+ .map(statement -> statement.replace(" ON test.DELETE", " ON DELETE"))
+ .map(statement -> statement.replace(" REFERENCES ", " REFERENCES test."))
+ .collect(Collectors.toList());
+ tables.add(0, "create schema test");
+ client
+ .updateDatabaseDdl(
+ database.getId().getInstanceId().getInstance(),
+ database.getId().getDatabase(),
+ tables,
+ null)
+ .get();
}
@Override
@@ -138,68 +171,70 @@ private Column(
@Test
public void testGetColumns() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getColumns(getDefaultCatalog(database), DEFAULT_SCHEMA, TABLE_WITH_ALL_COLS, null)) {
- int pos = 1;
- for (Column col : EXPECTED_COLUMNS) {
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals("tablewithallcolumntypes", rs.getString("TABLE_NAME"));
- assertEquals(col.name, rs.getString("COLUMN_NAME"));
- assertEquals(col.type, rs.getInt("DATA_TYPE"));
- assertEquals(col.typeName, rs.getString("TYPE_NAME"));
- if (col.colSize == null) {
- assertEquals(0, rs.getInt("COLUMN_SIZE"));
- assertTrue(rs.wasNull());
- } else {
- assertEquals(col.colSize.intValue(), rs.getInt("COLUMN_SIZE"));
- }
- rs.getObject("BUFFER_LENGTH"); // just assert that it exists
- if (col.decimalDigits == null) {
- assertEquals(0, rs.getInt("DECIMAL_DIGITS"));
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getColumns(getDefaultCatalog(database), schema, TABLE_WITH_ALL_COLS, null)) {
+ int pos = 1;
+ for (Column col : EXPECTED_COLUMNS) {
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals("tablewithallcolumntypes", rs.getString("TABLE_NAME"));
+ assertEquals(col.name, rs.getString("COLUMN_NAME"));
+ assertEquals(col.type, rs.getInt("DATA_TYPE"));
+ assertEquals(col.typeName, rs.getString("TYPE_NAME"));
+ if (col.colSize == null) {
+ assertEquals(0, rs.getInt("COLUMN_SIZE"));
+ assertTrue(rs.wasNull());
+ } else {
+ assertEquals(col.colSize.intValue(), rs.getInt("COLUMN_SIZE"));
+ }
+ rs.getObject("BUFFER_LENGTH"); // just assert that it exists
+ if (col.decimalDigits == null) {
+ assertEquals(0, rs.getInt("DECIMAL_DIGITS"));
+ assertTrue(rs.wasNull());
+ } else {
+ assertEquals(col.decimalDigits.intValue(), rs.getInt("DECIMAL_DIGITS"));
+ }
+ if (col.radix == null) {
+ assertEquals(0, rs.getInt("NUM_PREC_RADIX"));
+ assertTrue(rs.wasNull());
+ } else {
+ assertEquals(col.radix.intValue(), rs.getInt("NUM_PREC_RADIX"));
+ }
+ assertEquals(
+ "Nullable difference for " + col.name,
+ col.nullable ? DatabaseMetaData.columnNullable : DatabaseMetaData.columnNoNulls,
+ rs.getInt("NULLABLE"));
+ assertNull(rs.getString("REMARKS"));
+ assertNull(rs.getString("COLUMN_DEF"));
+ assertEquals(0, rs.getInt("SQL_DATA_TYPE"));
+ assertEquals(0, rs.getInt("SQL_DATETIME_SUB"));
+ if (col.charOctetLength == null) {
+ assertEquals(0, rs.getInt("CHAR_OCTET_LENGTH"));
+ assertTrue(rs.wasNull());
+ } else {
+ assertEquals(col.charOctetLength.intValue(), rs.getInt("CHAR_OCTET_LENGTH"));
+ }
+ assertEquals(pos, rs.getInt("ORDINAL_POSITION"));
+ assertEquals(col.nullable ? "YES" : "NO", rs.getString("IS_NULLABLE"));
+ assertNull(rs.getString("SCOPE_CATALOG"));
+ assertNull(rs.getString("SCOPE_SCHEMA"));
+ assertNull(rs.getString("SCOPE_TABLE"));
+
+ assertEquals((short) 0, rs.getShort("SOURCE_DATA_TYPE"));
assertTrue(rs.wasNull());
- } else {
- assertEquals(col.decimalDigits.intValue(), rs.getInt("DECIMAL_DIGITS"));
- }
- if (col.radix == null) {
- assertEquals(0, rs.getInt("NUM_PREC_RADIX"));
- assertTrue(rs.wasNull());
- } else {
- assertEquals(col.radix.intValue(), rs.getInt("NUM_PREC_RADIX"));
- }
- assertEquals(
- "Nullable difference for " + col.name,
- col.nullable ? DatabaseMetaData.columnNullable : DatabaseMetaData.columnNoNulls,
- rs.getInt("NULLABLE"));
- assertNull(rs.getString("REMARKS"));
- assertNull(rs.getString("COLUMN_DEF"));
- assertEquals(0, rs.getInt("SQL_DATA_TYPE"));
- assertEquals(0, rs.getInt("SQL_DATETIME_SUB"));
- if (col.charOctetLength == null) {
- assertEquals(0, rs.getInt("CHAR_OCTET_LENGTH"));
- assertTrue(rs.wasNull());
- } else {
- assertEquals(col.charOctetLength.intValue(), rs.getInt("CHAR_OCTET_LENGTH"));
- }
- assertEquals(pos, rs.getInt("ORDINAL_POSITION"));
- assertEquals(col.nullable ? "YES" : "NO", rs.getString("IS_NULLABLE"));
- assertNull(rs.getString("SCOPE_CATALOG"));
- assertNull(rs.getString("SCOPE_SCHEMA"));
- assertNull(rs.getString("SCOPE_TABLE"));
- assertEquals((short) 0, rs.getShort("SOURCE_DATA_TYPE"));
- assertTrue(rs.wasNull());
+ assertEquals("NO", rs.getString("IS_AUTOINCREMENT"));
+ assertEquals(col.computed ? "YES" : "NO", rs.getString("IS_GENERATEDCOLUMN"));
+ assertEquals(24, rs.getMetaData().getColumnCount());
- assertEquals("NO", rs.getString("IS_AUTOINCREMENT"));
- assertEquals(col.computed ? "YES" : "NO", rs.getString("IS_GENERATEDCOLUMN"));
- assertEquals(24, rs.getMetaData().getColumnCount());
-
- pos++;
+ pos++;
+ }
+ assertFalse(rs.next());
}
- assertFalse(rs.next());
}
}
}
@@ -207,168 +242,170 @@ public void testGetColumns() throws SQLException {
@Test
public void testGetCrossReferences() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- SINGERS_TABLE,
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- ALBUMS_TABLE)) {
- assertTrue(rs.next());
-
- assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
- assertEquals("singers", rs.getString("PKTABLE_NAME"));
- assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
- assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
- assertEquals("albums", rs.getString("FKTABLE_NAME"));
- assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
- assertEquals((short) 1, rs.getShort("KEY_SEQ"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertNotNull(rs.getString("FK_NAME"));
- assertEquals("PK_singers", rs.getString("PK_NAME"));
- assertEquals(
- (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- ALBUMS_TABLE,
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- SONGS_TABLE)) {
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
- assertEquals("albums", rs.getString("PKTABLE_NAME"));
- assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
- assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
- assertEquals("songs", rs.getString("FKTABLE_NAME"));
- assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
- assertEquals((short) 1, rs.getShort("KEY_SEQ"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertNotNull(rs.getString("FK_NAME"));
- assertEquals("PK_albums", rs.getString("PK_NAME"));
- assertEquals(
- (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ getDefaultCatalog(database),
+ schema,
+ SINGERS_TABLE,
+ getDefaultCatalog(database),
+ schema,
+ ALBUMS_TABLE)) {
+ assertTrue(rs.next());
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
- assertEquals("albums", rs.getString("PKTABLE_NAME"));
- assertEquals("albumid", rs.getString("PKCOLUMN_NAME"));
- assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
- assertEquals("songs", rs.getString("FKTABLE_NAME"));
- assertEquals("albumid", rs.getString("FKCOLUMN_NAME"));
- assertEquals((short) 2, rs.getShort("KEY_SEQ"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertNotNull(rs.getString("FK_NAME"));
- assertEquals("PK_albums", rs.getString("PK_NAME"));
- assertEquals(
- (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("singers", rs.getString("PKTABLE_NAME"));
+ assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
+ assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("albums", rs.getString("FKTABLE_NAME"));
+ assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
+ assertEquals((short) 1, rs.getShort("KEY_SEQ"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
+ assertNotNull(rs.getString("FK_NAME"));
+ assertEquals("PK_singers", rs.getString("PK_NAME"));
+ assertEquals(
+ (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ }
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ getDefaultCatalog(database),
+ schema,
+ ALBUMS_TABLE,
+ getDefaultCatalog(database),
+ schema,
+ SONGS_TABLE)) {
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("albums", rs.getString("PKTABLE_NAME"));
+ assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
+ assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("songs", rs.getString("FKTABLE_NAME"));
+ assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
+ assertEquals((short) 1, rs.getShort("KEY_SEQ"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
+ assertNotNull(rs.getString("FK_NAME"));
+ assertEquals("PK_albums", rs.getString("PK_NAME"));
+ assertEquals(
+ (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- assertFalse(rs.next());
- }
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("albums", rs.getString("PKTABLE_NAME"));
+ assertEquals("albumid", rs.getString("PKCOLUMN_NAME"));
+ assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("songs", rs.getString("FKTABLE_NAME"));
+ assertEquals("albumid", rs.getString("FKCOLUMN_NAME"));
+ assertEquals((short) 2, rs.getShort("KEY_SEQ"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
+ assertNotNull(rs.getString("FK_NAME"));
+ assertEquals("PK_albums", rs.getString("PK_NAME"));
+ assertEquals(
+ (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- SINGERS_TABLE,
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- CONCERTS_TABLE)) {
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
- assertEquals("singers", rs.getString("PKTABLE_NAME"));
- assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
- assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
- assertEquals("concerts", rs.getString("FKTABLE_NAME"));
- assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
- assertEquals((short) 1, rs.getShort("KEY_SEQ"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertNotNull(rs.getString("FK_NAME"));
- assertEquals("PK_singers", rs.getString("PK_NAME"));
- assertEquals(
- (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ assertFalse(rs.next());
+ }
- assertFalse(rs.next());
- }
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ getDefaultCatalog(database),
+ schema,
+ SINGERS_TABLE,
+ getDefaultCatalog(database),
+ schema,
+ CONCERTS_TABLE)) {
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("singers", rs.getString("PKTABLE_NAME"));
+ assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
+ assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("concerts", rs.getString("FKTABLE_NAME"));
+ assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
+ assertEquals((short) 1, rs.getShort("KEY_SEQ"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
+ assertNotNull(rs.getString("FK_NAME"));
+ assertEquals("PK_singers", rs.getString("PK_NAME"));
+ assertEquals(
+ (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- TABLE_WITH_ALL_COLS,
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- TABLE_WITH_REF)) {
+ assertFalse(rs.next());
+ }
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
- assertEquals("tablewithallcolumntypes", rs.getString("PKTABLE_NAME"));
- assertEquals("colfloat64", rs.getString("PKCOLUMN_NAME"));
- assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
- assertEquals("tablewithref", rs.getString("FKTABLE_NAME"));
- assertEquals("reffloat", rs.getString("FKCOLUMN_NAME"));
- assertEquals((short) 1, rs.getShort("KEY_SEQ"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertEquals("fk_tablewithref_tablewithallcolumntypes", rs.getString("FK_NAME"));
- assertEquals(
- (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ getDefaultCatalog(database),
+ schema,
+ TABLE_WITH_ALL_COLS,
+ getDefaultCatalog(database),
+ schema,
+ TABLE_WITH_REF)) {
+
+ assertTrue(schema, rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("tablewithallcolumntypes", rs.getString("PKTABLE_NAME"));
+ assertEquals("colfloat64", rs.getString("PKCOLUMN_NAME"));
+ assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("tablewithref", rs.getString("FKTABLE_NAME"));
+ assertEquals("reffloat", rs.getString("FKCOLUMN_NAME"));
+ assertEquals((short) 1, rs.getShort("KEY_SEQ"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
+ assertEquals("fk_tablewithref_tablewithallcolumntypes", rs.getString("FK_NAME"));
+ assertEquals(
+ (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
- assertEquals("tablewithallcolumntypes", rs.getString("PKTABLE_NAME"));
- assertEquals("colstring", rs.getString("PKCOLUMN_NAME"));
- assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
- assertEquals("tablewithref", rs.getString("FKTABLE_NAME"));
- assertEquals("refstring", rs.getString("FKCOLUMN_NAME"));
- assertEquals((short) 2, rs.getShort("KEY_SEQ"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
- assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
- assertEquals("fk_tablewithref_tablewithallcolumntypes", rs.getString("FK_NAME"));
- assertEquals(
- (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
+ assertEquals("tablewithallcolumntypes", rs.getString("PKTABLE_NAME"));
+ assertEquals("colstring", rs.getString("PKCOLUMN_NAME"));
+ assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
+ assertEquals("tablewithref", rs.getString("FKTABLE_NAME"));
+ assertEquals("refstring", rs.getString("FKCOLUMN_NAME"));
+ assertEquals((short) 2, rs.getShort("KEY_SEQ"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("UPDATE_RULE"));
+ assertEquals((short) DatabaseMetaData.importedKeyNoAction, rs.getShort("DELETE_RULE"));
+ assertEquals("fk_tablewithref_tablewithallcolumntypes", rs.getString("FK_NAME"));
+ assertEquals(
+ (short) DatabaseMetaData.importedKeyNotDeferrable, rs.getShort("DEFERRABILITY"));
- assertFalse(rs.next());
- }
- // try getting self-references
- try (ResultSet rs =
- connection
- .getMetaData()
- .getCrossReference(
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- ALBUMS_TABLE,
- getDefaultCatalog(database),
- DEFAULT_SCHEMA,
- ALBUMS_TABLE)) {
- assertFalse(rs.next());
+ assertFalse(rs.next());
+ }
+ // try getting self-references
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getCrossReference(
+ getDefaultCatalog(database),
+ schema,
+ ALBUMS_TABLE,
+ getDefaultCatalog(database),
+ schema,
+ ALBUMS_TABLE)) {
+ assertFalse(rs.next());
+ }
}
// try getting all cross-references in the database
try (ResultSet rs =
@@ -377,7 +414,7 @@ public void testGetCrossReferences() throws SQLException {
while (rs.next()) {
count++;
}
- assertEquals(6, count);
+ assertEquals(12, count);
}
}
}
@@ -442,36 +479,38 @@ private IndexInfo(
@Test
public void testGetIndexInfo() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getIndexInfo(getDefaultCatalog(database), DEFAULT_SCHEMA, null, false, false)) {
-
- for (IndexInfo index : EXPECTED_INDICES) {
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals(index.tableName, rs.getString("TABLE_NAME"));
- assertEquals(index.nonUnique, rs.getBoolean("NON_UNIQUE"));
- assertEquals(getDefaultCatalog(database), rs.getString("INDEX_QUALIFIER"));
-
- // Foreign key index names are automatically generated.
- if (!"FOREIGN_KEY".equals(index.indexName) && !"GENERATED".equals(index.indexName)) {
- assertEquals(index.indexName, rs.getString("INDEX_NAME"));
- }
- assertEquals(DatabaseMetaData.tableIndexHashed, rs.getShort("TYPE"));
- assertEquals(index.ordinalPosition, rs.getShort("ORDINAL_POSITION"));
- if (index.ordinalPosition == 0) {
- assertTrue(rs.wasNull());
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getIndexInfo(getDefaultCatalog(database), schema, null, false, false)) {
+
+ for (IndexInfo index : EXPECTED_INDICES) {
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals(index.tableName, rs.getString("TABLE_NAME"));
+ assertEquals(index.nonUnique, rs.getBoolean("NON_UNIQUE"));
+ assertEquals(getDefaultCatalog(database), rs.getString("INDEX_QUALIFIER"));
+
+ // Foreign key index names are automatically generated.
+ if (!"FOREIGN_KEY".equals(index.indexName) && !"GENERATED".equals(index.indexName)) {
+ assertEquals(index.indexName, rs.getString("INDEX_NAME"));
+ }
+ assertEquals(DatabaseMetaData.tableIndexHashed, rs.getShort("TYPE"));
+ assertEquals(index.ordinalPosition, rs.getShort("ORDINAL_POSITION"));
+ if (index.ordinalPosition == 0) {
+ assertTrue(rs.wasNull());
+ }
+ assertEquals(index.columnName, rs.getString("COLUMN_NAME"));
+ assertEquals(index.ascDesc, rs.getString("ASC_OR_DESC"));
+ assertEquals(-1, rs.getInt("CARDINALITY"));
+ assertEquals(-1, rs.getInt("PAGES"));
+ assertNull(rs.getString("FILTER_CONDITION"));
}
- assertEquals(index.columnName, rs.getString("COLUMN_NAME"));
- assertEquals(index.ascDesc, rs.getString("ASC_OR_DESC"));
- assertEquals(-1, rs.getInt("CARDINALITY"));
- assertEquals(-1, rs.getInt("PAGES"));
- assertNull(rs.getString("FILTER_CONDITION"));
+ // all indices found
+ assertFalse(rs.next());
}
- // all indices found
- assertFalse(rs.next());
}
}
}
@@ -479,17 +518,19 @@ public void testGetIndexInfo() throws SQLException {
@Test
public void testGetExportedKeys() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getExportedKeys(getDefaultCatalog(database), DEFAULT_SCHEMA, SINGERS_TABLE)) {
- assertExportedKeysSingers(rs);
- }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getExportedKeys(getDefaultCatalog(database), DEFAULT_SCHEMA, ALBUMS_TABLE)) {
- assertKeysAlbumsSongs(rs);
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getExportedKeys(getDefaultCatalog(database), schema, SINGERS_TABLE)) {
+ assertExportedKeysSingers(schema, rs);
+ }
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getExportedKeys(getDefaultCatalog(database), schema, ALBUMS_TABLE)) {
+ assertKeysAlbumsSongs(schema, rs);
+ }
}
}
}
@@ -497,51 +538,53 @@ public void testGetExportedKeys() throws SQLException {
@Test
public void testGetImportedKeys() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getImportedKeys(getDefaultCatalog(database), DEFAULT_SCHEMA, SINGERS_TABLE)) {
- assertImportedKeysSingers(rs);
- }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getImportedKeys(getDefaultCatalog(database), DEFAULT_SCHEMA, ALBUMS_TABLE)) {
- assertImportedKeysAlbums(rs);
- }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getImportedKeys(getDefaultCatalog(database), DEFAULT_SCHEMA, CONCERTS_TABLE)) {
- assertImportedKeysConcerts(rs);
- }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getImportedKeys(getDefaultCatalog(database), DEFAULT_SCHEMA, SONGS_TABLE)) {
- assertKeysAlbumsSongs(rs);
- }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getImportedKeys(getDefaultCatalog(database), DEFAULT_SCHEMA, TABLE_WITH_REF)) {
- assertImportedKeysTableWithRef(rs);
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getImportedKeys(getDefaultCatalog(database), schema, SINGERS_TABLE)) {
+ assertImportedKeysSingers(schema, rs);
+ }
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getImportedKeys(getDefaultCatalog(database), schema, ALBUMS_TABLE)) {
+ assertImportedKeysAlbums(schema, rs);
+ }
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getImportedKeys(getDefaultCatalog(database), schema, CONCERTS_TABLE)) {
+ assertImportedKeysConcerts(schema, rs);
+ }
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getImportedKeys(getDefaultCatalog(database), schema, SONGS_TABLE)) {
+ assertKeysAlbumsSongs(schema, rs);
+ }
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getImportedKeys(getDefaultCatalog(database), schema, TABLE_WITH_REF)) {
+ assertImportedKeysTableWithRef(schema, rs);
+ }
}
}
}
- private void assertImportedKeysSingers(ResultSet rs) throws SQLException {
+ private void assertImportedKeysSingers(String schema, ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertImportedKeysTableWithRef(ResultSet rs) throws SQLException {
+ private void assertImportedKeysTableWithRef(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals("tablewithallcolumntypes", rs.getString("PKTABLE_NAME"));
assertEquals("colfloat64", rs.getString("PKCOLUMN_NAME"));
assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals("tablewithref", rs.getString("FKTABLE_NAME"));
assertEquals("reffloat", rs.getString("FKCOLUMN_NAME"));
assertEquals((short) 1, rs.getShort("KEY_SEQ"));
@@ -553,11 +596,11 @@ private void assertImportedKeysTableWithRef(ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals("tablewithallcolumntypes", rs.getString("PKTABLE_NAME"));
assertEquals("colstring", rs.getString("PKCOLUMN_NAME"));
assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals("tablewithref", rs.getString("FKTABLE_NAME"));
assertEquals("refstring", rs.getString("FKCOLUMN_NAME"));
assertEquals((short) 2, rs.getShort("KEY_SEQ"));
@@ -570,14 +613,14 @@ private void assertImportedKeysTableWithRef(ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertImportedKeysAlbums(ResultSet rs) throws SQLException {
+ private void assertImportedKeysAlbums(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals("singers", rs.getString("PKTABLE_NAME"));
assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals("albums", rs.getString("FKTABLE_NAME"));
assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
assertEquals((short) 1, rs.getShort("KEY_SEQ"));
@@ -590,14 +633,14 @@ private void assertImportedKeysAlbums(ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertImportedKeysConcerts(ResultSet rs) throws SQLException {
+ private void assertImportedKeysConcerts(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals("singers", rs.getString("PKTABLE_NAME"));
assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals("concerts", rs.getString("FKTABLE_NAME"));
assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
assertEquals((short) 1, rs.getShort("KEY_SEQ"));
@@ -609,14 +652,14 @@ private void assertImportedKeysConcerts(ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertExportedKeysSingers(ResultSet rs) throws SQLException {
+ private void assertExportedKeysSingers(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals("singers", rs.getString("PKTABLE_NAME"));
assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals("albums", rs.getString("FKTABLE_NAME"));
assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
assertEquals((short) 1, rs.getShort("KEY_SEQ"));
@@ -628,11 +671,11 @@ private void assertExportedKeysSingers(ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals("singers", rs.getString("PKTABLE_NAME"));
assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals("concerts", rs.getString("FKTABLE_NAME"));
assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
assertEquals((short) 1, rs.getShort("KEY_SEQ"));
@@ -645,14 +688,14 @@ private void assertExportedKeysSingers(ResultSet rs) throws SQLException {
assertFalse(rs.next());
}
- private void assertKeysAlbumsSongs(ResultSet rs) throws SQLException {
+ private void assertKeysAlbumsSongs(String schema, ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals("albums", rs.getString("PKTABLE_NAME"));
assertEquals("singerid", rs.getString("PKCOLUMN_NAME"));
assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals("songs", rs.getString("FKTABLE_NAME"));
assertEquals("singerid", rs.getString("FKCOLUMN_NAME"));
assertEquals((short) 1, rs.getShort("KEY_SEQ"));
@@ -664,11 +707,11 @@ private void assertKeysAlbumsSongs(ResultSet rs) throws SQLException {
assertTrue(rs.next());
assertEquals(getDefaultCatalog(database), rs.getString("PKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("PKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("PKTABLE_SCHEM"));
assertEquals("albums", rs.getString("PKTABLE_NAME"));
assertEquals("albumid", rs.getString("PKCOLUMN_NAME"));
assertEquals(getDefaultCatalog(database), rs.getString("FKTABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("FKTABLE_SCHEM"));
+ assertEquals(schema, rs.getString("FKTABLE_SCHEM"));
assertEquals("songs", rs.getString("FKTABLE_NAME"));
assertEquals("albumid", rs.getString("FKCOLUMN_NAME"));
assertEquals((short) 2, rs.getShort("KEY_SEQ"));
@@ -684,58 +727,60 @@ private void assertKeysAlbumsSongs(ResultSet rs) throws SQLException {
@Test
public void testGetPrimaryKeys() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getPrimaryKeys(getDefaultCatalog(database), DEFAULT_SCHEMA, SINGERS_TABLE)) {
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals("singers", rs.getString("TABLE_NAME"));
- assertEquals("singerid", rs.getString("COLUMN_NAME"));
- assertEquals(1, rs.getInt("KEY_SEQ"));
- assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
- assertFalse(rs.next());
- }
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getPrimaryKeys(getDefaultCatalog(database), schema, SINGERS_TABLE)) {
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals("singers", rs.getString("TABLE_NAME"));
+ assertEquals("singerid", rs.getString("COLUMN_NAME"));
+ assertEquals(1, rs.getInt("KEY_SEQ"));
+ assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
+ assertFalse(rs.next());
+ }
- try (ResultSet rs =
- connection
- .getMetaData()
- .getPrimaryKeys(getDefaultCatalog(database), DEFAULT_SCHEMA, ALBUMS_TABLE)) {
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals("albums", rs.getString("TABLE_NAME"));
- assertEquals("singerid", rs.getString("COLUMN_NAME"));
- assertEquals(1, rs.getInt("KEY_SEQ"));
- assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals("albums", rs.getString("TABLE_NAME"));
- assertEquals("albumid", rs.getString("COLUMN_NAME"));
- assertEquals(2, rs.getInt("KEY_SEQ"));
- assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getPrimaryKeys(getDefaultCatalog(database), schema, ALBUMS_TABLE)) {
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals("albums", rs.getString("TABLE_NAME"));
+ assertEquals("singerid", rs.getString("COLUMN_NAME"));
+ assertEquals(1, rs.getInt("KEY_SEQ"));
+ assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals("albums", rs.getString("TABLE_NAME"));
+ assertEquals("albumid", rs.getString("COLUMN_NAME"));
+ assertEquals(2, rs.getInt("KEY_SEQ"));
+ assertEquals("PRIMARY_KEY", rs.getString("PK_NAME"));
- assertFalse(rs.next());
+ assertFalse(rs.next());
+ }
}
}
}
- @Ignore("Views are not yet supported for PostgreSQL")
@Test
public void testGetViews() throws SQLException {
try (Connection connection = createConnection(env, database)) {
- try (ResultSet rs =
- connection
- .getMetaData()
- .getTables(
- getDefaultCatalog(database), DEFAULT_SCHEMA, null, new String[] {"VIEW"})) {
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
- assertEquals(DEFAULT_SCHEMA, rs.getString("TABLE_SCHEM"));
- assertEquals("SingersView", rs.getString("TABLE_NAME"));
- assertFalse(rs.next());
+ for (String schema : new String[] {DEFAULT_SCHEMA, "test"}) {
+ try (ResultSet rs =
+ connection
+ .getMetaData()
+ .getTables(getDefaultCatalog(database), schema, null, new String[] {"VIEW"})) {
+ assertTrue(rs.next());
+ assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CAT"));
+ assertEquals(schema, rs.getString("TABLE_SCHEM"));
+ assertEquals("singersview", rs.getString("TABLE_NAME"));
+ assertFalse(rs.next());
+ }
}
}
}
@@ -744,24 +789,28 @@ public void testGetViews() throws SQLException {
public void testGetSchemas() throws SQLException {
try (Connection connection = createConnection(env, database)) {
assertEquals("public", connection.getSchema());
- try (ResultSet rs = connection.getMetaData().getSchemas()) {
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CATALOG"));
- assertEquals("information_schema", rs.getString("TABLE_SCHEM"));
+ try (ResultSet schemas = connection.getMetaData().getSchemas()) {
+ assertTrue(schemas.next());
+ assertEquals(getDefaultCatalog(database), schemas.getString("TABLE_CATALOG"));
+ assertEquals("information_schema", schemas.getString("TABLE_SCHEM"));
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CATALOG"));
- assertEquals("pg_catalog", rs.getString("TABLE_SCHEM"));
+ assertTrue(schemas.next());
+ assertEquals(getDefaultCatalog(database), schemas.getString("TABLE_CATALOG"));
+ assertEquals("pg_catalog", schemas.getString("TABLE_SCHEM"));
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CATALOG"));
- assertEquals("public", rs.getString("TABLE_SCHEM"));
+ assertTrue(schemas.next());
+ assertEquals(getDefaultCatalog(database), schemas.getString("TABLE_CATALOG"));
+ assertEquals("public", schemas.getString("TABLE_SCHEM"));
- assertTrue(rs.next());
- assertEquals(getDefaultCatalog(database), rs.getString("TABLE_CATALOG"));
- assertEquals("spanner_sys", rs.getString("TABLE_SCHEM"));
+ assertTrue(schemas.next());
+ assertEquals(getDefaultCatalog(database), schemas.getString("TABLE_CATALOG"));
+ assertEquals("spanner_sys", schemas.getString("TABLE_SCHEM"));
- assertFalse(rs.next());
+ assertTrue(schemas.next());
+ assertEquals(getDefaultCatalog(database), schemas.getString("TABLE_CATALOG"));
+ assertEquals("test", schemas.getString("TABLE_SCHEM"));
+
+ assertFalse(schemas.next());
}
}
}
@@ -799,8 +848,7 @@ private Table(String name, String type) {
new Table("all_nullable_types"),
new Table("concerts"),
new Table("singers"),
- // TODO: Enable when views are supported for PostgreSQL dialect databases.
- // new Table("singersview", "VIEW"),
+ new Table("singersview", "VIEW"),
new Table("songs"),
new Table("tablewithallcolumntypes"),
new Table("tablewithref"));
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITSingleJarTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITSingleJarTest.java
new file mode 100644
index 000000000..9ae8f6003
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITSingleJarTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2024 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 org.junit.Assert.assertEquals;
+
+import com.google.cloud.spanner.Database;
+import com.google.cloud.spanner.DatabaseId;
+import com.google.cloud.spanner.ParallelIntegrationTest;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.CharStreams;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests that the following works:
+ *
+ *
+ * - Build a single-jar-with-dependencies
+ *
- Compile a simple Java application consisting of a single file and no dependencies to a
+ * class file
+ *
- Run the simple Java application with only itself + the single-jar-with-dependencies on the
+ * class path
+ *
+ */
+@Category(ParallelIntegrationTest.class)
+@RunWith(JUnit4.class)
+public class ITSingleJarTest extends ITAbstractJdbcTest {
+ @ClassRule public static JdbcIntegrationTestEnv env = new JdbcIntegrationTestEnv();
+
+ private Database database;
+
+ @Before
+ public void setup() {
+ database =
+ env.getOrCreateDatabase(
+ getDialect(),
+ ImmutableList.of("create table test (id int64, value string(max)) primary key (id)"));
+ }
+
+ @Test
+ public void testUseSingleJar() throws Exception {
+ buildSingleJar();
+ buildMainClass();
+ runTestApplication();
+ }
+
+ @Test
+ public void testUseShadedJar() throws Exception {
+ buildShadedJar();
+ buildMainClass();
+ runTestApplication();
+ }
+
+ private void runTestApplication() throws Exception {
+ DatabaseId id = database.getId();
+ ProcessBuilder builder = new ProcessBuilder();
+ if (System.getenv("SPANNER_EMULATOR_HOST") != null) {
+ builder.environment().put("SPANNER_EMULATOR_HOST", System.getenv("SPANNER_EMULATOR_HOST"));
+ }
+ // This runs the simple test application with only the shaded jar on the classpath.
+ builder.command(
+ "java",
+ "-cp",
+ "./target/single/test/:target/single/single.jar",
+ "com/google/cloud/spanner/jdbc/SingleJarTestApplication",
+ id.getInstanceId().getProject(),
+ id.getInstanceId().getInstance(),
+ id.getDatabase());
+ execute(builder);
+ }
+
+ private void buildSingleJar() throws Exception {
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command("mvn", "clean", "package", "-DskipTests", "-Dalt.build.dir=./target/single");
+ execute(builder);
+ }
+
+ private void buildShadedJar() throws Exception {
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command(
+ "mvn", "clean", "-Pshade", "package", "-DskipTests", "-Dalt.build.dir=./target/single");
+ execute(builder);
+ }
+
+ private void buildMainClass() throws Exception {
+ Files.createDirectories(Paths.get("target", "single", "test"));
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command(
+ "javac",
+ "src/test/java/com/google/cloud/spanner/jdbc/SingleJarTestApplication.java",
+ "-d",
+ "./target/single/test");
+ execute(builder);
+ }
+
+ private void execute(ProcessBuilder builder) throws Exception {
+ Process process = builder.start();
+ String errors;
+ try (InputStreamReader reader = new InputStreamReader(process.getErrorStream())) {
+ errors = CharStreams.toString(reader);
+ }
+ assertEquals(errors, 0, process.waitFor());
+ }
+}
diff --git a/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables_PG.sql b/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables_PG.sql
index abecd9fad..3aafc5722 100644
--- a/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables_PG.sql
+++ b/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables_PG.sql
@@ -26,6 +26,11 @@ CREATE TABLE Singers (
CREATE INDEX SingersByFirstLastName ON Singers(FirstName, LastName);
+CREATE VIEW SingersView SQL SECURITY INVOKER AS
+SELECT s.SingerId AS SingerId, s.FirstName AS FirstName, s.LastName AS LastName
+FROM Singers s
+ORDER BY s.LastName, s.FirstName;
+
CREATE TABLE Albums (
SingerId BIGINT NOT NULL,
AlbumId BIGINT NOT NULL,
diff --git a/versions.txt b/versions.txt
index 6f8ffa714..05d8b85f0 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-spanner-jdbc:2.20.2:2.20.2
+google-cloud-spanner-jdbc:2.21.0:2.21.0