From 7adca95cf6b230b9197668c174610e207cd46715 Mon Sep 17 00:00:00 2001
From: "release-please[bot]"
<55107282+release-please[bot]@users.noreply.github.com>
Date: Fri, 22 Mar 2024 15:24:16 +0000
Subject: [PATCH 01/29] chore(main): release 2.16.2-SNAPSHOT (#1542)
:robot: I have created a release *beep* *boop*
---
### Updating meta-information for bleeding-edge SNAPSHOT release.
---
This PR was generated with [Release Please](https://togithub.com/googleapis/release-please). See [documentation](https://togithub.com/googleapis/release-please#release-please).
---
pom.xml | 2 +-
samples/snapshot/pom.xml | 2 +-
versions.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/pom.xml b/pom.xml
index 0d3858e85..841d2648b 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.16.1
+ 2.16.2-SNAPSHOT
jar
Google Cloud Spanner JDBC
https://github.com/googleapis/java-spanner-jdbc
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 2cf73702e..baaf8bb89 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -28,7 +28,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.16.1
+ 2.16.2-SNAPSHOT
diff --git a/versions.txt b/versions.txt
index 5aa7bb71c..21a50e390 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-spanner-jdbc:2.16.1:2.16.1
+google-cloud-spanner-jdbc:2.16.1:2.16.2-SNAPSHOT
From 5b8fd2e2f100312a05fd8401a05385ae5306aa33 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Mon, 25 Mar 2024 16:19:10 +0100
Subject: [PATCH 02/29] chore(deps): update dependency
com.google.cloud:google-cloud-spanner-jdbc to v2.16.1 (#1543)
---
samples/install-without-bom/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index a52052ae6..a2537a7e6 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.16.0
+ 2.16.1
From fb8377edbb8ab115657a831c1b5c1a892e7be172 Mon Sep 17 00:00:00 2001
From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com>
Date: Thu, 28 Mar 2024 20:43:15 +0100
Subject: [PATCH 03/29] chore: update ignore paths in renovate config (#1945)
(#1545)
Source-Link: https://github.com/googleapis/synthtool/commit/571a0916913e0aff0f66ca513514072893d534eb
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-java:latest@sha256:81c3ec554428c8ff6c92f0d58668b7ef52265d053a82284c97a326745e786949
Co-authored-by: Owl Bot
---
.github/.OwlBot.lock.yaml | 4 ++--
renovate.json | 14 +++++++++++++-
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml
index db1099bec..6167135e9 100644
--- a/.github/.OwlBot.lock.yaml
+++ b/.github/.OwlBot.lock.yaml
@@ -13,5 +13,5 @@
# limitations under the License.
docker:
image: gcr.io/cloud-devrel-public-resources/owlbot-java:latest
- digest: sha256:084ad4c60551b075846bcb2405ec1c14b0d00ec1eb5503d4dd0d2a92cdc2d3e2
-# created: 2024-03-15T14:33:32.257974519Z
+ digest: sha256:81c3ec554428c8ff6c92f0d58668b7ef52265d053a82284c97a326745e786949
+# created: 2024-03-27T17:59:25.436353226Z
diff --git a/renovate.json b/renovate.json
index 15ce4ecae..d47519b15 100644
--- a/renovate.json
+++ b/renovate.json
@@ -12,7 +12,10 @@
],
"ignorePaths": [
".kokoro/requirements.txt",
- ".github/workflows/**"
+ ".github/workflows/approve-readme.yaml",
+ ".github/workflows/ci.yaml",
+ ".github/workflows/renovate_config_check.yaml",
+ ".github/workflows/samples.yaml"
],
"customManagers": [
{
@@ -23,6 +26,15 @@
"matchStrings": ["value: \"gcr.io/cloud-devrel-public-resources/graalvm.*:(?.*?)\""],
"depNameTemplate": "com.google.cloud:sdk-platform-java-config",
"datasourceTemplate": "maven"
+ },
+ {
+ "customType": "regex",
+ "fileMatch": [
+ "^.github/workflows/unmanaged_dependency_check.yaml$"
+ ],
+ "matchStrings": ["uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v(?.+?)\\n"],
+ "depNameTemplate": "com.google.cloud:sdk-platform-java-config",
+ "datasourceTemplate": "maven"
}
],
"packageRules": [
From 36be4e113c4b46523061c89b70f3d748a814ba2a Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Mon, 1 Apr 2024 08:41:03 +0200
Subject: [PATCH 04/29] chore(deps): update dependency
com.google.cloud:libraries-bom to v26.35.0 (#1551)
---
samples/snippets/pom.xml | 2 +-
samples/spring-data-jdbc/pom.xml | 2 +-
samples/spring-data-mybatis/pom.xml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 6c9638733..f36192293 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -30,7 +30,7 @@
com.google.cloud
libraries-bom
- 26.34.0
+ 26.35.0
pom
import
diff --git a/samples/spring-data-jdbc/pom.xml b/samples/spring-data-jdbc/pom.xml
index 34664f77b..4d35a2087 100644
--- a/samples/spring-data-jdbc/pom.xml
+++ b/samples/spring-data-jdbc/pom.xml
@@ -30,7 +30,7 @@
com.google.cloud
libraries-bom
- 26.34.0
+ 26.35.0
import
pom
diff --git a/samples/spring-data-mybatis/pom.xml b/samples/spring-data-mybatis/pom.xml
index 4f8dccf9d..1ec0e4b92 100644
--- a/samples/spring-data-mybatis/pom.xml
+++ b/samples/spring-data-mybatis/pom.xml
@@ -35,7 +35,7 @@
com.google.cloud
libraries-bom
- 26.34.0
+ 26.35.0
import
pom
From ac75b9faf0eaeb499428ecefda1f3285b3d28e67 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Mon, 1 Apr 2024 08:41:30 +0200
Subject: [PATCH 05/29] deps: update dependency
com.google.cloud:google-cloud-spanner-bom to v6.63.0 (#1552)
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 841d2648b..38d49832b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,7 +62,7 @@
com.google.cloud
google-cloud-spanner-bom
- 6.62.0
+ 6.63.0
pom
import
From 18c5ad4d4124f095547d50c0d2e154bc06380642 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Mon, 1 Apr 2024 09:01:25 +0200
Subject: [PATCH 06/29] deps: update actions/checkout digest to b4ffde6 (#1546)
---
.github/workflows/integration-tests-against-emulator.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/integration-tests-against-emulator.yaml b/.github/workflows/integration-tests-against-emulator.yaml
index f299be4f9..eff700ea5 100644
--- a/.github/workflows/integration-tests-against-emulator.yaml
+++ b/.github/workflows/integration-tests-against-emulator.yaml
@@ -16,7 +16,7 @@ jobs:
- 9020:9020
steps:
- - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
+ - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- uses: stCarolas/setup-maven@v4
with:
maven-version: 3.8.1
From 736e3afa54149dd11803bd715569afd9ec8e87f2 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Mon, 1 Apr 2024 09:01:48 +0200
Subject: [PATCH 07/29] deps: update actions/checkout action to v4 (#1547)
---
.github/workflows/spring-data-jdbc-sample.yaml | 2 +-
.github/workflows/spring-data-mybatis-sample.yaml | 2 +-
.github/workflows/unmanaged-dependency-check.yaml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/spring-data-jdbc-sample.yaml b/.github/workflows/spring-data-jdbc-sample.yaml
index 028a48631..fc488ab89 100644
--- a/.github/workflows/spring-data-jdbc-sample.yaml
+++ b/.github/workflows/spring-data-jdbc-sample.yaml
@@ -20,7 +20,7 @@ jobs:
spring-data-jdbc:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
distribution: temurin
diff --git a/.github/workflows/spring-data-mybatis-sample.yaml b/.github/workflows/spring-data-mybatis-sample.yaml
index 39be0e6e6..636f71258 100644
--- a/.github/workflows/spring-data-mybatis-sample.yaml
+++ b/.github/workflows/spring-data-mybatis-sample.yaml
@@ -20,7 +20,7 @@ jobs:
spring-data-jdbc:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
distribution: temurin
diff --git a/.github/workflows/unmanaged-dependency-check.yaml b/.github/workflows/unmanaged-dependency-check.yaml
index 4ca518aea..a8a2e2ee3 100644
--- a/.github/workflows/unmanaged-dependency-check.yaml
+++ b/.github/workflows/unmanaged-dependency-check.yaml
@@ -5,7 +5,7 @@ jobs:
unmanaged_dependency_check:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
distribution: temurin
From d1d422cdf0a74231c468262662fdf5ce4d27b8ef Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Mon, 1 Apr 2024 09:02:14 +0200
Subject: [PATCH 08/29] deps: update actions/github-script action to v7 (#1548)
---
.github/workflows/auto-release.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml
index 7a106d007..18d92e5a2 100644
--- a/.github/workflows/auto-release.yaml
+++ b/.github/workflows/auto-release.yaml
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
if: contains(github.head_ref, 'release-please')
steps:
- - uses: actions/github-script@v6
+ - uses: actions/github-script@v7
with:
github-token: ${{secrets.YOSHI_APPROVER_TOKEN}}
debug: true
From 961222c15e09ddee3f3fbc2354fcaa9bba3ecfe1 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Thu, 4 Apr 2024 17:42:18 +0200
Subject: [PATCH 09/29] chore(deps): update dependency
com.google.cloud:libraries-bom to v26.36.0 (#1555)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.cloud:libraries-bom](https://cloud.google.com/java/docs/bom) ([source](https://togithub.com/googleapis/java-cloud-bom)) | `26.35.0` -> `26.36.0` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) |
---
### Release Notes
googleapis/java-cloud-bom (com.google.cloud:libraries-bom)
### [`v26.36.0`](https://togithub.com/googleapis/java-cloud-bom/blob/HEAD/CHANGELOG.md#26360-2024-04-03)
[Compare Source](https://togithub.com/googleapis/java-cloud-bom/compare/v26.35.0...v26.36.0)
##### Dependencies
- update dependency com.google.cloud:gapic-libraries-bom to v1.34.0 ([#6519](https://togithub.com/googleapis/java-cloud-bom/issues/6519)) ([602ba0c](https://togithub.com/googleapis/java-cloud-bom/commit/602ba0c894123089b0c8eb297d32c638265ccf03))
---
### Configuration
π
**Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).
π¦ **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
β» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
π **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] If you want to rebase/retry this PR, check this box
---
This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/googleapis/java-spanner-jdbc).
---
samples/snippets/pom.xml | 2 +-
samples/spring-data-jdbc/pom.xml | 2 +-
samples/spring-data-mybatis/pom.xml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index f36192293..dfe37f9db 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -30,7 +30,7 @@
com.google.cloud
libraries-bom
- 26.35.0
+ 26.36.0
pom
import
diff --git a/samples/spring-data-jdbc/pom.xml b/samples/spring-data-jdbc/pom.xml
index 4d35a2087..ed88ebf92 100644
--- a/samples/spring-data-jdbc/pom.xml
+++ b/samples/spring-data-jdbc/pom.xml
@@ -30,7 +30,7 @@
com.google.cloud
libraries-bom
- 26.35.0
+ 26.36.0
import
pom
diff --git a/samples/spring-data-mybatis/pom.xml b/samples/spring-data-mybatis/pom.xml
index 1ec0e4b92..4aeb75380 100644
--- a/samples/spring-data-mybatis/pom.xml
+++ b/samples/spring-data-mybatis/pom.xml
@@ -35,7 +35,7 @@
com.google.cloud
libraries-bom
- 26.35.0
+ 26.36.0
import
pom
From 25be8ea07b3f9ea0138ee3580bf65fddc166f2c7 Mon Sep 17 00:00:00 2001
From: Joe Wang <106995533+JoeWang1127@users.noreply.github.com>
Date: Thu, 4 Apr 2024 20:41:24 +0000
Subject: [PATCH 10/29] chore: update version of unmanaged dependency check
(#1556)
---
...ed-dependency-check.yaml => unmanaged_dependency_check.yaml} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename .github/workflows/{unmanaged-dependency-check.yaml => unmanaged_dependency_check.yaml} (87%)
diff --git a/.github/workflows/unmanaged-dependency-check.yaml b/.github/workflows/unmanaged_dependency_check.yaml
similarity index 87%
rename from .github/workflows/unmanaged-dependency-check.yaml
rename to .github/workflows/unmanaged_dependency_check.yaml
index a8a2e2ee3..4406831cb 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@unmanaged-dependencies-check-latest
+ uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.28.1
with:
bom-path: pom.xml
From 121d08e16db0bbb1f6041a201d620829e7121f4d Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Fri, 5 Apr 2024 06:59:37 +0200
Subject: [PATCH 11/29] deps: update stcarolas/setup-maven action to v5 (#1550)
---
.github/workflows/integration-tests-against-emulator.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/integration-tests-against-emulator.yaml b/.github/workflows/integration-tests-against-emulator.yaml
index eff700ea5..0a330e1a8 100644
--- a/.github/workflows/integration-tests-against-emulator.yaml
+++ b/.github/workflows/integration-tests-against-emulator.yaml
@@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- - uses: stCarolas/setup-maven@v4
+ - uses: stCarolas/setup-maven@v5
with:
maven-version: 3.8.1
- uses: actions/setup-java@v1
From cb2b911b0b332e97f85974ec880a5ab7a12a7578 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Fri, 5 Apr 2024 09:57:31 +0200
Subject: [PATCH 12/29] deps: update actions/setup-java action to v4 (#1549)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* deps: update actions/setup-java action to v4
* build: add distribution + actual version number
* build: remove redundant Maven setup
---------
Co-authored-by: Knut Olav LΓΈite
---
.github/workflows/integration-tests-against-emulator.yaml | 8 +++-----
.github/workflows/spring-data-jdbc-sample.yaml | 2 +-
.github/workflows/spring-data-mybatis-sample.yaml | 2 +-
.github/workflows/unmanaged_dependency_check.yaml | 2 +-
4 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/integration-tests-against-emulator.yaml b/.github/workflows/integration-tests-against-emulator.yaml
index 0a330e1a8..ccf1ebd1f 100644
--- a/.github/workflows/integration-tests-against-emulator.yaml
+++ b/.github/workflows/integration-tests-against-emulator.yaml
@@ -16,12 +16,10 @@ jobs:
- 9020:9020
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- - uses: stCarolas/setup-maven@v5
- with:
- maven-version: 3.8.1
- - uses: actions/setup-java@v1
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
with:
+ distribution: temurin
java-version: 8
- run: java -version
- run: .kokoro/build.sh
diff --git a/.github/workflows/spring-data-jdbc-sample.yaml b/.github/workflows/spring-data-jdbc-sample.yaml
index fc488ab89..0185a2905 100644
--- a/.github/workflows/spring-data-jdbc-sample.yaml
+++ b/.github/workflows/spring-data-jdbc-sample.yaml
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-java@v3
+ - uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
diff --git a/.github/workflows/spring-data-mybatis-sample.yaml b/.github/workflows/spring-data-mybatis-sample.yaml
index 636f71258..5267d6f59 100644
--- a/.github/workflows/spring-data-mybatis-sample.yaml
+++ b/.github/workflows/spring-data-mybatis-sample.yaml
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-java@v3
+ - uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml
index 4406831cb..c724f3dab 100644
--- a/.github/workflows/unmanaged_dependency_check.yaml
+++ b/.github/workflows/unmanaged_dependency_check.yaml
@@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-java@v3
+ - uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 11
From 32a6fa3b315911a18f38a2ecfd909263b704238d Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Fri, 5 Apr 2024 19:08:00 +0200
Subject: [PATCH 13/29] chore(deps): update dependency
com.google.cloud:libraries-bom to v26.37.0 (#1557)
---
samples/snippets/pom.xml | 2 +-
samples/spring-data-jdbc/pom.xml | 2 +-
samples/spring-data-mybatis/pom.xml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index dfe37f9db..29bc0371c 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -30,7 +30,7 @@
com.google.cloud
libraries-bom
- 26.36.0
+ 26.37.0
pom
import
diff --git a/samples/spring-data-jdbc/pom.xml b/samples/spring-data-jdbc/pom.xml
index ed88ebf92..c9fbf898d 100644
--- a/samples/spring-data-jdbc/pom.xml
+++ b/samples/spring-data-jdbc/pom.xml
@@ -30,7 +30,7 @@
com.google.cloud
libraries-bom
- 26.36.0
+ 26.37.0
import
pom
diff --git a/samples/spring-data-mybatis/pom.xml b/samples/spring-data-mybatis/pom.xml
index 4aeb75380..16e8c3d37 100644
--- a/samples/spring-data-mybatis/pom.xml
+++ b/samples/spring-data-mybatis/pom.xml
@@ -35,7 +35,7 @@
com.google.cloud
libraries-bom
- 26.36.0
+ 26.37.0
import
pom
From 6f85f5c785b5bed9983544e7baabba82a2e4e05f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?=
Date: Tue, 9 Apr 2024 14:07:54 +0200
Subject: [PATCH 14/29] chore: add connection helper to get Spanner instance
(#1558)
Add a connection helper to get the underlying Spanner instance.
This helper will be removed once the getSpanner() method has
been made public in the Java client library.
---
.../spanner/connection/ConnectionHelper.java | 31 +++++++++++++++++++
.../spanner/jdbc/AbstractJdbcConnection.java | 6 ++++
2 files changed, 37 insertions(+)
create mode 100644 src/main/java/com/google/cloud/spanner/connection/ConnectionHelper.java
diff --git a/src/main/java/com/google/cloud/spanner/connection/ConnectionHelper.java b/src/main/java/com/google/cloud/spanner/connection/ConnectionHelper.java
new file mode 100644
index 000000000..5f71ec605
--- /dev/null
+++ b/src/main/java/com/google/cloud/spanner/connection/ConnectionHelper.java
@@ -0,0 +1,31 @@
+/*
+ * 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.connection;
+
+import com.google.cloud.spanner.Spanner;
+
+/** Static helper class to get the {@link Spanner} instance from the underlying connection. */
+public class ConnectionHelper {
+
+ /** Private constructor to prevent instantiation. */
+ private ConnectionHelper() {}
+
+ public static Spanner getSpanner(Connection connection) {
+ // TODO: Remove once getSpanner() has been added to the public interface.
+ return ((ConnectionImpl) connection).getSpanner();
+ }
+}
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java
index 33cf3bc57..340b6d409 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java
@@ -17,8 +17,10 @@
package com.google.cloud.spanner.jdbc;
import com.google.cloud.spanner.Dialect;
+import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.connection.AbstractStatementParser;
+import com.google.cloud.spanner.connection.ConnectionHelper;
import com.google.cloud.spanner.connection.ConnectionOptions;
import com.google.common.annotations.VisibleForTesting;
import com.google.rpc.Code;
@@ -78,6 +80,10 @@ ConnectionOptions getConnectionOptions() {
return options;
}
+ Spanner getSpanner() {
+ return ConnectionHelper.getSpanner(this.spanner);
+ }
+
@Override
public Dialect getDialect() {
return spanner.getDialect();
From 194c8205dee9cc4144b18e219df43027b9f15cf2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?=
Date: Wed, 10 Apr 2024 14:28:02 +0200
Subject: [PATCH 15/29] docs: create samples for quickstart guide (#1536)
* docs: create samples for quickstart guide
* docs: add createConnection sample
* docs: add sample for DML
* docs: add sample for mutations
* feat: add dml batch sample
* feat: add more samples
* feat: add more samples
* feat: add data boost sample
* chore: fix style
* chore: add run commands
* fix: shorten method length
* test: add sample test runner
* fix: duplicate name
* chore: fix tag prefix
* chore: add more commands
* feat: add sample for emulator
* feat: add tagging sample
* chore: use fixed port binding for now
* chore: remove changes to JdbcDriver
---
.github/workflows/sample-tests.yml | 30 +
samples/snippets/java.header | 15 +
samples/snippets/license-checks.xml | 10 +
samples/snippets/pom.xml | 92 +-
.../com/example/spanner/jdbc/JdbcSample.java | 1830 +++++++++++++++++
.../example/spanner/jdbc/package-info.java | 18 +
.../example/spanner/jdbc/JdbcSampleTest.java | 396 ++++
7 files changed, 2382 insertions(+), 9 deletions(-)
create mode 100644 .github/workflows/sample-tests.yml
create mode 100644 samples/snippets/java.header
create mode 100644 samples/snippets/license-checks.xml
create mode 100644 samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java
create mode 100644 samples/snippets/src/main/java/com/example/spanner/jdbc/package-info.java
create mode 100644 samples/snippets/src/test/java/com/example/spanner/jdbc/JdbcSampleTest.java
diff --git a/.github/workflows/sample-tests.yml b/.github/workflows/sample-tests.yml
new file mode 100644
index 000000000..cec19c7a2
--- /dev/null
+++ b/.github/workflows/sample-tests.yml
@@ -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: samples
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v3
+ with:
+ distribution: temurin
+ java-version: 8
+ - name: Run sample tests
+ run: mvn --quiet --batch-mode test
+ working-directory: samples/snippets
diff --git a/samples/snippets/java.header b/samples/snippets/java.header
new file mode 100644
index 000000000..d0970ba7d
--- /dev/null
+++ b/samples/snippets/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/snippets/license-checks.xml b/samples/snippets/license-checks.xml
new file mode 100644
index 000000000..a7a611940
--- /dev/null
+++ b/samples/snippets/license-checks.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 29bc0371c..047c0c761 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -1,20 +1,16 @@
4.0.0
- com.google.cloud
spanner-jdbc-snippets
jar
Google Google Cloud Spanner JDBC Snippets
https://github.com/googleapis/java-spanner-jdbc
-
- com.google.cloud.samples
- shared-configuration
- 1.2.0
+ com.google.cloud
+ sdk-platform-java-config
+ 3.27.0
+
@@ -42,6 +38,84 @@
com.google.cloud
google-cloud-spanner-jdbc
+
+
+
+ com.google.cloud
+ google-cloud-spanner
+
+
+
+ org.testcontainers
+ testcontainers
+ 1.19.7
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
-
+
+
+
+
+ maven-resources-plugin
+
+
+ copy-resources
+ validate
+
+ copy-resources
+
+
+ ${project.build.directory}/jdbc-snippets
+
+
+ resources
+ true
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-dependencies
+ prepare-package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/jdbc-snippets/lib
+ false
+ false
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+ jdbc-snippets/jdbc-samples
+
+ false
+
+ com.example.spanner.jdbc.JdbcSample
+ true
+ lib/
+
+
+
+
+
+
diff --git a/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java b/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java
new file mode 100644
index 000000000..2e46058de
--- /dev/null
+++ b/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java
@@ -0,0 +1,1830 @@
+/*
+ * 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.example.spanner.jdbc;
+
+import com.google.api.gax.core.NoCredentialsProvider;
+import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
+import com.google.cloud.spanner.DatabaseId;
+import com.google.cloud.spanner.Mutation;
+import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.SpannerOptions;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminSettings;
+import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
+import com.google.spanner.admin.database.v1.DatabaseDialect;
+import com.google.spanner.admin.instance.v1.InstanceName;
+import com.google.spanner.v1.DatabaseName;
+import io.grpc.ManagedChannelBuilder;
+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.Arrays;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+
+public final class JdbcSample {
+ static class Singer {
+
+ /** Primary key in the Singers table. */
+ private final long singerId;
+
+ /** Mapped to the FirstName column. */
+ private final String firstName;
+
+ /** Mapped to the FirstName column. */
+ private final String lastName;
+
+ Singer(final long id, final String first, final String last) {
+ this.singerId = id;
+ this.firstName = first;
+ this.lastName = last;
+ }
+
+ public long getSingerId() {
+ return singerId;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+ }
+
+ static class Album {
+
+ /** The first part of the primary key of Albums. */
+ private final long singerId;
+
+ /** The second part of the primary key of Albums. */
+ private final long albumId;
+
+ /** Mapped to the AlbumTitle column. */
+ private final String albumTitle;
+
+ Album(final long singer, final long album, final String title) {
+ this.singerId = singer;
+ this.albumId = album;
+ this.albumTitle = title;
+ }
+
+ public long getSingerId() {
+ return singerId;
+ }
+
+ public long getAlbumId() {
+ return albumId;
+ }
+
+ public String getAlbumTitle() {
+ return albumTitle;
+ }
+ }
+
+ // [START spanner_insert_data]
+ // [START spanner_postgresql_insert_data]
+ /** The list of Singers to insert. */
+ static final List SINGERS =
+ Arrays.asList(
+ new Singer(1, "Marc", "Richards"),
+ new Singer(2, "Catalina", "Smith"),
+ new Singer(3, "Alice", "Trentor"),
+ new Singer(4, "Lea", "Martin"),
+ new Singer(5, "David", "Lomond"));
+
+ /** The list of Albums to insert. */
+ static final List ALBUMS =
+ Arrays.asList(
+ new Album(1, 1, "Total Junk"),
+ new Album(1, 2, "Go, Go, Go"),
+ new Album(2, 1, "Green"),
+ new Album(2, 2, "Forever Hold Your Peace"),
+ new Album(2, 3, "Terrified"));
+
+ // [END spanner_insert_data]
+ // [END spanner_postgresql_insert_data]
+
+ private JdbcSample() {
+ }
+
+ // [START spanner_create_database]
+ static void createDatabase(
+ final DatabaseAdminClient dbAdminClient,
+ final InstanceName instanceName,
+ final String databaseId,
+ final Properties properties) throws SQLException {
+ // Use the Spanner admin client to create a database.
+ CreateDatabaseRequest createDatabaseRequest =
+ CreateDatabaseRequest.newBuilder()
+ .setCreateStatement("CREATE DATABASE `" + databaseId + "`")
+ .setParent(instanceName.toString())
+ .build();
+ try {
+ dbAdminClient.createDatabaseAsync(createDatabaseRequest).get();
+ } catch (ExecutionException e) {
+ throw SpannerExceptionFactory.asSpannerException(e.getCause());
+ } catch (InterruptedException e) {
+ throw SpannerExceptionFactory.propagateInterrupt(e);
+ }
+
+ // Connect to the database with the JDBC driver and create two test tables.
+ String projectId = instanceName.getProject();
+ String instanceId = instanceName.getInstance();
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ projectId, instanceId, databaseId),
+ properties)) {
+ try (Statement statement = connection.createStatement()) {
+ // Create the tables in one batch.
+ statement.addBatch(
+ "CREATE TABLE Singers ("
+ + " SingerId INT64 NOT NULL,"
+ + " FirstName STRING(1024),"
+ + " LastName STRING(1024),"
+ + " SingerInfo BYTES(MAX),"
+ + " FullName STRING(2048) AS "
+ + " (ARRAY_TO_STRING([FirstName, LastName], \" \")) STORED"
+ + ") PRIMARY KEY (SingerId)");
+ statement.addBatch(
+ "CREATE TABLE Albums ("
+ + " SingerId INT64 NOT NULL,"
+ + " AlbumId INT64 NOT NULL,"
+ + " AlbumTitle STRING(MAX)"
+ + ") PRIMARY KEY (SingerId, AlbumId),"
+ + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE");
+ statement.executeBatch();
+ }
+ }
+ System.out.printf(
+ "Created database [%s]\n",
+ DatabaseName.of(projectId, instanceId, databaseId));
+ }
+ // [END spanner_create_database]
+
+ // [START spanner_postgresql_create_database]
+ static void createPostgreSQLDatabase(
+ final DatabaseAdminClient dbAdminClient,
+ final InstanceName instanceName,
+ final String databaseId,
+ final Properties properties) throws SQLException {
+ // Use the Spanner admin client to create a database.
+ CreateDatabaseRequest createDatabaseRequest =
+ CreateDatabaseRequest.newBuilder()
+ // PostgreSQL database names and other identifiers
+ // must be quoted using double quotes.
+ .setCreateStatement("create database \"" + databaseId + "\"")
+ .setParent(instanceName.toString())
+ .setDatabaseDialect(DatabaseDialect.POSTGRESQL)
+ .build();
+ try {
+ dbAdminClient.createDatabaseAsync(createDatabaseRequest).get();
+ } catch (ExecutionException e) {
+ throw SpannerExceptionFactory.asSpannerException(e.getCause());
+ } catch (InterruptedException e) {
+ throw SpannerExceptionFactory.propagateInterrupt(e);
+ }
+
+ // Connect to the database with the JDBC driver and create two test tables.
+ String projectId = instanceName.getProject();
+ String instanceId = instanceName.getInstance();
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ projectId, instanceId, databaseId),
+ properties)) {
+ try (Statement statement = connection.createStatement()) {
+ // Create the tables in one batch.
+ statement.addBatch(
+ "create table singers ("
+ + " singer_id bigint primary key not null,"
+ + " first_name varchar(1024),"
+ + " last_name varchar(1024),"
+ + " singer_info bytea,"
+ + " full_name varchar(2048) generated always as (\n"
+ + " case when first_name is null then last_name\n"
+ + " when last_name is null then first_name\n"
+ + " else first_name || ' ' || last_name\n"
+ + " end) stored"
+ + ")");
+ statement.addBatch(
+ "create table albums ("
+ + " singer_id bigint not null,"
+ + " album_id bigint not null,"
+ + " album_title varchar,"
+ + " primary key (singer_id, album_id)"
+ + ") interleave in parent singers on delete cascade");
+ statement.executeBatch();
+ }
+ }
+ System.out.printf(
+ "Created database [%s]\n",
+ DatabaseName.of(projectId, instanceId, databaseId));
+ }
+ // [END spanner_postgresql_create_database]
+
+ // [START spanner_create_jdbc_connection]
+ static void createConnection(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ // Connection properties can be specified both with in a Properties object
+ // and in the connection URL.
+ properties.put("numChannels", "8");
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s"
+ + ";minSessions=400;maxSessions=400",
+ project, instance, database),
+ properties)) {
+ try (ResultSet resultSet =
+ connection.createStatement().executeQuery("select 'Hello World!'")) {
+ while (resultSet.next()) {
+ System.out.println(resultSet.getString(1));
+ }
+ }
+ }
+ }
+ // [END spanner_create_jdbc_connection]
+
+ // [START spanner_create_jdbc_connection_with_emulator]
+ static void createConnectionWithEmulator(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ // Add autoConfigEmulator=true to the connection URL to instruct the JDBC
+ // driver to connect to the Spanner emulator on localhost:9010.
+ // The Spanner instance and database are automatically created if these
+ // don't already exist.
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s"
+ + ";autoConfigEmulator=true",
+ project, instance, database),
+ properties)) {
+ try (ResultSet resultSet =
+ connection.createStatement().executeQuery("select 'Hello World!'")) {
+ while (resultSet.next()) {
+ System.out.println(resultSet.getString(1));
+ }
+ }
+ }
+ }
+ // [END spanner_create_jdbc_connection_with_emulator]
+
+ // [START spanner_dml_getting_started_insert]
+ static void writeDataWithDml(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Add 4 rows in one statement.
+ // JDBC always uses '?' as a parameter placeholder.
+ try (PreparedStatement preparedStatement =
+ connection.prepareStatement(
+ "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES "
+ + "(?, ?, ?), "
+ + "(?, ?, ?), "
+ + "(?, ?, ?), "
+ + "(?, ?, ?)")) {
+
+ final ImmutableList singers =
+ ImmutableList.of(
+ new Singer(/* SingerId = */ 12L, "Melissa", "Garcia"),
+ new Singer(/* SingerId = */ 13L, "Russel", "Morales"),
+ new Singer(/* SingerId = */ 14L, "Jacqueline", "Long"),
+ new Singer(/* SingerId = */ 15L, "Dylan", "Shaw"));
+
+ // Note that JDBC parameters start at index 1.
+ int paramIndex = 0;
+ for (Singer singer : singers) {
+ preparedStatement.setLong(++paramIndex, singer.singerId);
+ preparedStatement.setString(++paramIndex, singer.firstName);
+ preparedStatement.setString(++paramIndex, singer.lastName);
+ }
+
+ int updateCount = preparedStatement.executeUpdate();
+ System.out.printf("%d records inserted.\n", updateCount);
+ }
+ }
+ }
+ // [END spanner_dml_getting_started_insert]
+
+ // [START spanner_postgresql_dml_getting_started_insert]
+ static void writeDataWithDmlPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Add 4 rows in one statement.
+ // JDBC always uses '?' as a parameter placeholder.
+ try (PreparedStatement preparedStatement =
+ connection.prepareStatement(
+ "INSERT INTO singers (singer_id, first_name, last_name) VALUES "
+ + "(?, ?, ?), "
+ + "(?, ?, ?), "
+ + "(?, ?, ?), "
+ + "(?, ?, ?)")) {
+
+ final ImmutableList singers =
+ ImmutableList.of(
+ new Singer(/* SingerId = */ 12L, "Melissa", "Garcia"),
+ new Singer(/* SingerId = */ 13L, "Russel", "Morales"),
+ new Singer(/* SingerId = */ 14L, "Jacqueline", "Long"),
+ new Singer(/* SingerId = */ 15L, "Dylan", "Shaw"));
+
+ // Note that JDBC parameters start at index 1.
+ int paramIndex = 0;
+ for (Singer singer : singers) {
+ preparedStatement.setLong(++paramIndex, singer.singerId);
+ preparedStatement.setString(++paramIndex, singer.firstName);
+ preparedStatement.setString(++paramIndex, singer.lastName);
+ }
+
+ int updateCount = preparedStatement.executeUpdate();
+ System.out.printf("%d records inserted.\n", updateCount);
+ }
+ }
+ }
+ // [END spanner_postgresql_dml_getting_started_insert]
+
+ // [START spanner_dml_batch]
+ static void writeDataWithDmlBatch(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Add multiple rows in one DML batch.
+ // JDBC always uses '?' as a parameter placeholder.
+ try (PreparedStatement preparedStatement =
+ connection.prepareStatement(
+ "INSERT INTO Singers (SingerId, FirstName, LastName) "
+ + "VALUES (?, ?, ?)")) {
+ final ImmutableList singers =
+ ImmutableList.of(
+ new Singer(/* SingerId = */ 16L, "Sarah", "Wilson"),
+ new Singer(/* SingerId = */ 17L, "Ethan", "Miller"),
+ new Singer(/* SingerId = */ 18L, "Maya", "Patel"));
+
+ for (Singer singer : singers) {
+ // Note that JDBC parameters start at index 1.
+ int paramIndex = 0;
+ preparedStatement.setLong(++paramIndex, singer.singerId);
+ preparedStatement.setString(++paramIndex, singer.firstName);
+ preparedStatement.setString(++paramIndex, singer.lastName);
+ preparedStatement.addBatch();
+ }
+
+ int[] updateCounts = preparedStatement.executeBatch();
+ System.out.printf(
+ "%d records inserted.\n",
+ Arrays.stream(updateCounts).sum());
+ }
+ }
+ }
+ // [END spanner_dml_batch]
+
+ // [START spanner_postgresql_dml_batch]
+ static void writeDataWithDmlBatchPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Add multiple rows in one DML batch.
+ // JDBC always uses '?' as a parameter placeholder.
+ try (PreparedStatement preparedStatement =
+ connection.prepareStatement(
+ "INSERT INTO singers (singer_id, first_name, last_name)"
+ + " VALUES (?, ?, ?)")) {
+ final ImmutableList singers =
+ ImmutableList.of(
+ new Singer(/* SingerId = */ 16L, "Sarah", "Wilson"),
+ new Singer(/* SingerId = */ 17L, "Ethan", "Miller"),
+ new Singer(/* SingerId = */ 18L, "Maya", "Patel"));
+
+ for (Singer singer : singers) {
+ // Note that JDBC parameters start at index 1.
+ int paramIndex = 0;
+ preparedStatement.setLong(++paramIndex, singer.singerId);
+ preparedStatement.setString(++paramIndex, singer.firstName);
+ preparedStatement.setString(++paramIndex, singer.lastName);
+ preparedStatement.addBatch();
+ }
+
+ int[] updateCounts = preparedStatement.executeBatch();
+ System.out.printf(
+ "%d records inserted.\n",
+ Arrays.stream(updateCounts).sum());
+ }
+ }
+ }
+ // [END spanner_postgresql_dml_batch]
+
+ // [START spanner_insert_data]
+ static void writeDataWithMutations(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Unwrap the CloudSpannerJdbcConnection interface
+ // from the java.sql.Connection.
+ CloudSpannerJdbcConnection cloudSpannerJdbcConnection =
+ connection.unwrap(CloudSpannerJdbcConnection.class);
+
+ List mutations = new ArrayList<>();
+ for (Singer singer : SINGERS) {
+ mutations.add(
+ Mutation.newInsertBuilder("Singers")
+ .set("SingerId")
+ .to(singer.singerId)
+ .set("FirstName")
+ .to(singer.firstName)
+ .set("LastName")
+ .to(singer.lastName)
+ .build());
+ }
+ for (Album album : ALBUMS) {
+ mutations.add(
+ Mutation.newInsertBuilder("Albums")
+ .set("SingerId")
+ .to(album.singerId)
+ .set("AlbumId")
+ .to(album.albumId)
+ .set("AlbumTitle")
+ .to(album.albumTitle)
+ .build());
+ }
+ // Apply the mutations atomically to Spanner.
+ cloudSpannerJdbcConnection.write(mutations);
+ System.out.printf("Inserted %d rows.\n", mutations.size());
+ }
+ }
+ // [END spanner_insert_data]
+
+ // [START spanner_postgresql_insert_data]
+ static void writeDataWithMutationsPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Unwrap the CloudSpannerJdbcConnection interface
+ // from the java.sql.Connection.
+ CloudSpannerJdbcConnection cloudSpannerJdbcConnection =
+ connection.unwrap(CloudSpannerJdbcConnection.class);
+
+ List mutations = new ArrayList<>();
+ for (Singer singer : SINGERS) {
+ mutations.add(
+ Mutation.newInsertBuilder("singers")
+ .set("singer_id")
+ .to(singer.singerId)
+ .set("first_name")
+ .to(singer.firstName)
+ .set("last_name")
+ .to(singer.lastName)
+ .build());
+ }
+ for (Album album : ALBUMS) {
+ mutations.add(
+ Mutation.newInsertBuilder("albums")
+ .set("singer_id")
+ .to(album.singerId)
+ .set("album_id")
+ .to(album.albumId)
+ .set("album_title")
+ .to(album.albumTitle)
+ .build());
+ }
+ // Apply the mutations atomically to Spanner.
+ cloudSpannerJdbcConnection.write(mutations);
+ System.out.printf("Inserted %d rows.\n", mutations.size());
+ }
+ }
+ // [END spanner_postgresql_insert_data]
+
+ // [START spanner_query_data]
+ static void queryData(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "SELECT SingerId, AlbumId, AlbumTitle "
+ + "FROM Albums")) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %d %s\n",
+ resultSet.getLong("SingerId"),
+ resultSet.getLong("AlbumId"),
+ resultSet.getString("AlbumTitle"));
+ }
+ }
+ }
+ }
+ // [END spanner_query_data]
+
+ // [START spanner_postgresql_query_data]
+ static void queryDataPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "SELECT singer_id, album_id, album_title "
+ + "FROM albums")) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %d %s\n",
+ resultSet.getLong("singer_id"),
+ resultSet.getLong("album_id"),
+ resultSet.getString("album_title"));
+ }
+ }
+ }
+ }
+ // [END spanner_postgresql_query_data]
+
+ // [START spanner_query_with_parameter]
+ static void queryWithParameter(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ try (PreparedStatement statement =
+ connection.prepareStatement(
+ "SELECT SingerId, FirstName, LastName "
+ + "FROM Singers "
+ + "WHERE LastName = ?")) {
+ statement.setString(1, "Garcia");
+ try (ResultSet resultSet = statement.executeQuery()) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %s %s\n",
+ resultSet.getLong("SingerId"),
+ resultSet.getString("FirstName"),
+ resultSet.getString("LastName"));
+ }
+ }
+ }
+ }
+ }
+ // [END spanner_query_with_parameter]
+
+ // [START spanner_postgresql_query_with_parameter]
+ static void queryWithParameterPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ try (PreparedStatement statement =
+ connection.prepareStatement(
+ "SELECT singer_id, first_name, last_name "
+ + "FROM singers "
+ + "WHERE last_name = ?")) {
+ statement.setString(1, "Garcia");
+ try (ResultSet resultSet = statement.executeQuery()) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %s %s\n",
+ resultSet.getLong("singer_id"),
+ resultSet.getString("first_name"),
+ resultSet.getString("last_name"));
+ }
+ }
+ }
+ }
+ }
+ // [END spanner_postgresql_query_with_parameter]
+
+ // [START spanner_add_column]
+ static void addColumn(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ connection
+ .createStatement()
+ .execute("ALTER TABLE Albums ADD COLUMN MarketingBudget INT64");
+ System.out.println("Added MarketingBudget column");
+ }
+ }
+ // [END spanner_add_column]
+
+ // [START spanner_postgresql_add_column]
+ static void addColumnPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ connection
+ .createStatement()
+ .execute("alter table albums add column marketing_budget bigint");
+ System.out.println("Added marketing_budget column");
+ }
+ }
+ // [END spanner_postgresql_add_column]
+
+ // [START spanner_ddl_batch]
+ static void ddlBatch(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ try (Statement statement = connection.createStatement()) {
+ // Create two new tables in one batch.
+ statement.addBatch(
+ "CREATE TABLE Venues ("
+ + " VenueId INT64 NOT NULL,"
+ + " Name STRING(1024),"
+ + " Description JSON"
+ + ") PRIMARY KEY (VenueId)");
+ statement.addBatch(
+ "CREATE TABLE Concerts ("
+ + " ConcertId INT64 NOT NULL,"
+ + " VenueId INT64 NOT NULL,"
+ + " SingerId INT64 NOT NULL,"
+ + " StartTime TIMESTAMP,"
+ + " EndTime TIMESTAMP,"
+ + " CONSTRAINT Fk_Concerts_Venues FOREIGN KEY"
+ + " (VenueId) REFERENCES Venues (VenueId),"
+ + " CONSTRAINT Fk_Concerts_Singers FOREIGN KEY"
+ + " (SingerId) REFERENCES Singers (SingerId),"
+ + ") PRIMARY KEY (ConcertId)");
+ statement.executeBatch();
+ }
+ System.out.println("Added Venues and Concerts tables");
+ }
+ }
+ // [END spanner_ddl_batch]
+
+ // [START spanner_postgresql_ddl_batch]
+ static void ddlBatchPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ try (Statement statement = connection.createStatement()) {
+ // Create two new tables in one batch.
+ statement.addBatch(
+ "CREATE TABLE venues ("
+ + " venue_id bigint not null primary key,"
+ + " name varchar(1024),"
+ + " description jsonb"
+ + ")");
+ statement.addBatch(
+ "CREATE TABLE concerts ("
+ + " concert_id bigint not null primary key ,"
+ + " venue_id bigint not null,"
+ + " singer_id bigint not null,"
+ + " start_time timestamptz,"
+ + " end_time timestamptz,"
+ + " constraint fk_concerts_venues foreign key"
+ + " (venue_id) references venues (venue_id),"
+ + " constraint fk_concerts_singers foreign key"
+ + " (singer_id) references singers (singer_id)"
+ + ")");
+ statement.executeBatch();
+ }
+ System.out.println("Added venues and concerts tables");
+ }
+ }
+ // [END spanner_postgresql_ddl_batch]
+
+ // [START spanner_update_data]
+ static void updateDataWithMutations(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Unwrap the CloudSpannerJdbcConnection interface
+ // from the java.sql.Connection.
+ CloudSpannerJdbcConnection cloudSpannerJdbcConnection =
+ connection.unwrap(CloudSpannerJdbcConnection.class);
+
+ final long marketingBudgetAlbum1 = 100000L;
+ final long marketingBudgetAlbum2 = 500000L;
+ // Mutation can be used to update/insert/delete a single row in a table.
+ // Here we use newUpdateBuilder to create update mutations.
+ List mutations =
+ Arrays.asList(
+ Mutation.newUpdateBuilder("Albums")
+ .set("SingerId")
+ .to(1)
+ .set("AlbumId")
+ .to(1)
+ .set("MarketingBudget")
+ .to(marketingBudgetAlbum1)
+ .build(),
+ Mutation.newUpdateBuilder("Albums")
+ .set("SingerId")
+ .to(2)
+ .set("AlbumId")
+ .to(2)
+ .set("MarketingBudget")
+ .to(marketingBudgetAlbum2)
+ .build());
+ // This writes all the mutations to Cloud Spanner atomically.
+ cloudSpannerJdbcConnection.write(mutations);
+ System.out.println("Updated albums");
+ }
+ }
+ // [END spanner_update_data]
+
+ // [START spanner_postgresql_update_data]
+ static void updateDataWithMutationsPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Unwrap the CloudSpannerJdbcConnection interface
+ // from the java.sql.Connection.
+ CloudSpannerJdbcConnection cloudSpannerJdbcConnection =
+ connection.unwrap(CloudSpannerJdbcConnection.class);
+
+ final long marketingBudgetAlbum1 = 100000L;
+ final long marketingBudgetAlbum2 = 500000L;
+ // Mutation can be used to update/insert/delete a single row in a table.
+ // Here we use newUpdateBuilder to create update mutations.
+ List mutations =
+ Arrays.asList(
+ Mutation.newUpdateBuilder("albums")
+ .set("singer_id")
+ .to(1)
+ .set("album_id")
+ .to(1)
+ .set("marketing_budget")
+ .to(marketingBudgetAlbum1)
+ .build(),
+ Mutation.newUpdateBuilder("albums")
+ .set("singer_id")
+ .to(2)
+ .set("album_id")
+ .to(2)
+ .set("marketing_budget")
+ .to(marketingBudgetAlbum2)
+ .build());
+ // This writes all the mutations to Cloud Spanner atomically.
+ cloudSpannerJdbcConnection.write(mutations);
+ System.out.println("Updated albums");
+ }
+ }
+ // [END spanner_postgresql_update_data]
+
+ // [START spanner_query_data_with_new_column]
+ static void queryDataWithNewColumn(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Rows without an explicit value for MarketingBudget will have a
+ // MarketingBudget equal to null.
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "SELECT SingerId, AlbumId, MarketingBudget "
+ + "FROM Albums")) {
+ while (resultSet.next()) {
+ // Use the ResultSet#getObject(String) method to get data
+ // of any type from the ResultSet.
+ System.out.printf(
+ "%s %s %s\n",
+ resultSet.getObject("SingerId"),
+ resultSet.getObject("AlbumId"),
+ resultSet.getObject("MarketingBudget"));
+ }
+ }
+ }
+ }
+ // [END spanner_query_data_with_new_column]
+
+ // [START spanner_postgresql_query_data_with_new_column]
+ static void queryDataWithNewColumnPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Rows without an explicit value for marketing_budget will have a
+ // marketing_budget equal to null.
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "select singer_id, album_id, marketing_budget "
+ + "from albums")) {
+ while (resultSet.next()) {
+ // Use the ResultSet#getObject(String) method to get data
+ // of any type from the ResultSet.
+ System.out.printf(
+ "%s %s %s\n",
+ resultSet.getObject("singer_id"),
+ resultSet.getObject("album_id"),
+ resultSet.getObject("marketing_budget"));
+ }
+ }
+ }
+ }
+ // [END spanner_postgresql_query_data_with_new_column]
+
+ // [START spanner_dml_getting_started_update]
+ static void writeWithTransactionUsingDml(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Set AutoCommit=false to enable transactions.
+ connection.setAutoCommit(false);
+
+ // Transfer marketing budget from one album to another.
+ // We do it in a transaction to ensure that the transfer is atomic.
+ // There is no need to explicitly start the transaction. The first
+ // statement on the connection will start a transaction when
+ // AutoCommit=false.
+ String selectMarketingBudgetSql =
+ "SELECT MarketingBudget "
+ + "FROM Albums "
+ + "WHERE SingerId = ? AND AlbumId = ?";
+ long album2Budget = 0;
+ try (PreparedStatement selectMarketingBudgetStatement =
+ connection.prepareStatement(selectMarketingBudgetSql)) {
+ // Bind the query parameters to SingerId=2 and AlbumId=2.
+ selectMarketingBudgetStatement.setLong(1, 2);
+ selectMarketingBudgetStatement.setLong(2, 2);
+ try (ResultSet resultSet =
+ selectMarketingBudgetStatement.executeQuery()) {
+ while (resultSet.next()) {
+ album2Budget = resultSet.getLong("MarketingBudget");
+ }
+ }
+ // The transaction will only be committed if this condition still holds
+ // at the time of commit. Otherwise, the transaction will be aborted.
+ final long transfer = 200000;
+ if (album2Budget >= transfer) {
+ long album1Budget = 0;
+ // Re-use the existing PreparedStatement for selecting the
+ // MarketingBudget to get the budget for Album 1.
+ // Bind the query parameters to SingerId=1 and AlbumId=1.
+ selectMarketingBudgetStatement.setLong(1, 1);
+ selectMarketingBudgetStatement.setLong(2, 1);
+ try (ResultSet resultSet =
+ selectMarketingBudgetStatement.executeQuery()) {
+ while (resultSet.next()) {
+ album1Budget = resultSet.getLong("MarketingBudget");
+ }
+ }
+
+ // Transfer part of the marketing budget of Album 2 to Album 1.
+ album1Budget += transfer;
+ album2Budget -= transfer;
+ String updateSql =
+ "UPDATE Albums "
+ + "SET MarketingBudget = ? "
+ + "WHERE SingerId = ? and AlbumId = ?";
+ try (PreparedStatement updateStatement =
+ connection.prepareStatement(updateSql)) {
+ // Update Album 1.
+ int paramIndex = 0;
+ updateStatement.setLong(++paramIndex, album1Budget);
+ updateStatement.setLong(++paramIndex, 1);
+ updateStatement.setLong(++paramIndex, 1);
+ // Create a DML batch by calling addBatch on
+ // the current PreparedStatement.
+ updateStatement.addBatch();
+
+ // Update Album 2 in the same DML batch.
+ paramIndex = 0;
+ updateStatement.setLong(++paramIndex, album2Budget);
+ updateStatement.setLong(++paramIndex, 2);
+ updateStatement.setLong(++paramIndex, 2);
+ updateStatement.addBatch();
+
+ // Execute both DML statements in one batch.
+ updateStatement.executeBatch();
+ }
+ }
+ }
+ // Commit the current transaction.
+ connection.commit();
+ System.out.println(
+ "Transferred marketing budget from Album 2 to Album 1");
+ }
+ }
+ // [END spanner_dml_getting_started_update]
+
+ // [START spanner_postgresql_dml_getting_started_update]
+ static void writeWithTransactionUsingDmlPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Set AutoCommit=false to enable transactions.
+ connection.setAutoCommit(false);
+
+ // Transfer marketing budget from one album to another. We do it in a
+ // transaction to ensure that the transfer is atomic. There is no need
+ // to explicitly start the transaction. The first statement on the
+ // connection will start a transaction when AutoCommit=false.
+ String selectMarketingBudgetSql =
+ "SELECT marketing_budget "
+ + "from albums "
+ + "WHERE singer_id = ? and album_id = ?";
+ long album2Budget = 0;
+ try (PreparedStatement selectMarketingBudgetStatement =
+ connection.prepareStatement(selectMarketingBudgetSql)) {
+ // Bind the query parameters to SingerId=2 and AlbumId=2.
+ selectMarketingBudgetStatement.setLong(1, 2);
+ selectMarketingBudgetStatement.setLong(2, 2);
+ try (ResultSet resultSet =
+ selectMarketingBudgetStatement.executeQuery()) {
+ while (resultSet.next()) {
+ album2Budget = resultSet.getLong("marketing_budget");
+ }
+ }
+ // The transaction will only be committed if this condition still holds
+ // at the time of commit. Otherwise, the transaction will be aborted.
+ final long transfer = 200000;
+ if (album2Budget >= transfer) {
+ long album1Budget = 0;
+ // Re-use the existing PreparedStatement for selecting the
+ // marketing_budget to get the budget for Album 1.
+ // Bind the query parameters to SingerId=1 and AlbumId=1.
+ selectMarketingBudgetStatement.setLong(1, 1);
+ selectMarketingBudgetStatement.setLong(2, 1);
+ try (ResultSet resultSet =
+ selectMarketingBudgetStatement.executeQuery()) {
+ while (resultSet.next()) {
+ album1Budget = resultSet.getLong("marketing_budget");
+ }
+ }
+
+ // Transfer part of the marketing budget of Album 2 to Album 1.
+ album1Budget += transfer;
+ album2Budget -= transfer;
+ String updateSql =
+ "UPDATE albums "
+ + "SET marketing_budget = ? "
+ + "WHERE singer_id = ? and album_id = ?";
+ try (PreparedStatement updateStatement =
+ connection.prepareStatement(updateSql)) {
+ // Update Album 1.
+ int paramIndex = 0;
+ updateStatement.setLong(++paramIndex, album1Budget);
+ updateStatement.setLong(++paramIndex, 1);
+ updateStatement.setLong(++paramIndex, 1);
+ // Create a DML batch by calling addBatch
+ // on the current PreparedStatement.
+ updateStatement.addBatch();
+
+ // Update Album 2 in the same DML batch.
+ paramIndex = 0;
+ updateStatement.setLong(++paramIndex, album2Budget);
+ updateStatement.setLong(++paramIndex, 2);
+ updateStatement.setLong(++paramIndex, 2);
+ updateStatement.addBatch();
+
+ // Execute both DML statements in one batch.
+ updateStatement.executeBatch();
+ }
+ }
+ }
+ // Commit the current transaction.
+ connection.commit();
+ System.out.println(
+ "Transferred marketing budget from Album 2 to Album 1");
+ }
+ }
+ // [END spanner_postgresql_dml_getting_started_update]
+
+ // [START spanner_transaction_and_statement_tag]
+ static void tags(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Set AutoCommit=false to enable transactions.
+ connection.setAutoCommit(false);
+ // Set the TRANSACTION_TAG session variable to set a transaction tag
+ // for the current transaction.
+ connection
+ .createStatement()
+ .execute("SET TRANSACTION_TAG='example-tx-tag'");
+
+ // Set the STATEMENT_TAG session variable to set the request tag
+ // that should be included with the next SQL statement.
+ connection
+ .createStatement()
+ .execute("SET STATEMENT_TAG='query-marketing-budget'");
+ long marketingBudget = 0L;
+ long singerId = 1L;
+ long albumId = 1L;
+ try (PreparedStatement statement = connection.prepareStatement(
+ "SELECT MarketingBudget "
+ + "FROM Albums "
+ + "WHERE SingerId=? AND AlbumId=?")) {
+ statement.setLong(1, singerId);
+ statement.setLong(2, albumId);
+ try (ResultSet albumResultSet = statement.executeQuery()) {
+ while (albumResultSet.next()) {
+ marketingBudget = albumResultSet.getLong(1);
+ }
+ }
+ }
+ // Reduce the marketing budget by 10% if it is more than 1,000.
+ final long maxMarketingBudget = 1000L;
+ final float reduction = 0.1f;
+ if (marketingBudget > maxMarketingBudget) {
+ marketingBudget -= (long) (marketingBudget * reduction);
+ connection
+ .createStatement()
+ .execute("SET STATEMENT_TAG='reduce-marketing-budget'");
+ try (PreparedStatement statement = connection.prepareStatement(
+ "UPDATE Albums SET MarketingBudget=? "
+ + "WHERE SingerId=? AND AlbumId=?")) {
+ int paramIndex = 0;
+ statement.setLong(++paramIndex, marketingBudget);
+ statement.setLong(++paramIndex, singerId);
+ statement.setLong(++paramIndex, albumId);
+ statement.executeUpdate();
+ }
+ }
+
+ // Commit the current transaction.
+ connection.commit();
+ System.out.println("Reduced marketing budget");
+ }
+ }
+ // [END spanner_transaction_and_statement_tag]
+
+ // [START spanner_postgresql_transaction_and_statement_tag]
+ static void tagsPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Set AutoCommit=false to enable transactions.
+ connection.setAutoCommit(false);
+ // Set the TRANSACTION_TAG session variable to set a transaction tag
+ // for the current transaction.
+ connection
+ .createStatement()
+ .execute("set spanner.transaction_TAG='example-tx-tag'");
+
+ // Set the STATEMENT_TAG session variable to set the request tag
+ // that should be included with the next SQL statement.
+ connection
+ .createStatement()
+ .execute("set spanner.statement_tag='query-marketing-budget'");
+ long marketingBudget = 0L;
+ long singerId = 1L;
+ long albumId = 1L;
+ try (PreparedStatement statement = connection.prepareStatement(
+ "select marketing_budget "
+ + "from albums "
+ + "where singer_id=? and album_id=?")) {
+ statement.setLong(1, singerId);
+ statement.setLong(2, albumId);
+ try (ResultSet albumResultSet = statement.executeQuery()) {
+ while (albumResultSet.next()) {
+ marketingBudget = albumResultSet.getLong(1);
+ }
+ }
+ }
+ // Reduce the marketing budget by 10% if it is more than 1,000.
+ final long maxMarketingBudget = 1000L;
+ final float reduction = 0.1f;
+ if (marketingBudget > maxMarketingBudget) {
+ marketingBudget -= (long) (marketingBudget * reduction);
+ connection
+ .createStatement()
+ .execute("set spanner.statement_tag='reduce-marketing-budget'");
+ try (PreparedStatement statement = connection.prepareStatement(
+ "update albums set marketing_budget=? "
+ + "where singer_id=? AND album_id=?")) {
+ int paramIndex = 0;
+ statement.setLong(++paramIndex, marketingBudget);
+ statement.setLong(++paramIndex, singerId);
+ statement.setLong(++paramIndex, albumId);
+ statement.executeUpdate();
+ }
+ }
+
+ // Commit the current transaction.
+ connection.commit();
+ System.out.println("Reduced marketing budget");
+ }
+ }
+ // [END spanner_postgresql_transaction_and_statement_tag]
+
+ // [START spanner_read_only_transaction]
+ static void readOnlyTransaction(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Set AutoCommit=false to enable transactions.
+ connection.setAutoCommit(false);
+ // This SQL statement instructs the JDBC driver to use
+ // a read-only transaction.
+ connection.createStatement().execute("SET TRANSACTION READ ONLY");
+
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "SELECT SingerId, AlbumId, AlbumTitle "
+ + "FROM Albums "
+ + "ORDER BY SingerId, AlbumId")) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %d %s\n",
+ resultSet.getLong("SingerId"),
+ resultSet.getLong("AlbumId"),
+ resultSet.getString("AlbumTitle"));
+ }
+ }
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "SELECT SingerId, AlbumId, AlbumTitle "
+ + "FROM Albums "
+ + "ORDER BY AlbumTitle")) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %d %s\n",
+ resultSet.getLong("SingerId"),
+ resultSet.getLong("AlbumId"),
+ resultSet.getString("AlbumTitle"));
+ }
+ }
+ // End the read-only transaction by calling commit().
+ connection.commit();
+ }
+ }
+ // [END spanner_read_only_transaction]
+
+ // [START spanner_postgresql_read_only_transaction]
+ static void readOnlyTransactionPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Set AutoCommit=false to enable transactions.
+ connection.setAutoCommit(false);
+ // This SQL statement instructs the JDBC driver to use
+ // a read-only transaction.
+ connection.createStatement().execute("set transaction read only");
+
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "SELECT singer_id, album_id, album_title "
+ + "FROM albums "
+ + "ORDER BY singer_id, album_id")) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %d %s\n",
+ resultSet.getLong("singer_id"),
+ resultSet.getLong("album_id"),
+ resultSet.getString("album_title"));
+ }
+ }
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "SELECT singer_id, album_id, album_title "
+ + "FROM albums "
+ + "ORDER BY album_title")) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %d %s\n",
+ resultSet.getLong("singer_id"),
+ resultSet.getLong("album_id"),
+ resultSet.getString("album_title"));
+ }
+ }
+ // End the read-only transaction by calling commit().
+ connection.commit();
+ }
+ }
+ // [END spanner_postgresql_read_only_transaction]
+
+ // [START spanner_data_boost]
+ static void dataBoost(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // This enables Data Boost for all partitioned queries on this connection.
+ connection.createStatement().execute("SET DATA_BOOST_ENABLED=TRUE");
+
+ // Run a partitioned query. This query will use Data Boost.
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "RUN PARTITIONED QUERY "
+ + "SELECT SingerId, FirstName, LastName "
+ + "FROM Singers")) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %s %s\n",
+ resultSet.getLong("SingerId"),
+ resultSet.getString("FirstName"),
+ resultSet.getString("LastName"));
+ }
+ }
+ }
+ }
+ // [END spanner_data_boost]
+
+ // [START spanner_postgresql_data_boost]
+ static void dataBoostPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // This enables Data Boost for all partitioned queries on this connection.
+ connection
+ .createStatement()
+ .execute("set spanner.data_boost_enabled=true");
+
+ // Run a partitioned query. This query will use Data Boost.
+ try (ResultSet resultSet =
+ connection
+ .createStatement()
+ .executeQuery(
+ "run partitioned query "
+ + "select singer_id, first_name, last_name "
+ + "from singers")) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %s %s\n",
+ resultSet.getLong("singer_id"),
+ resultSet.getString("first_name"),
+ resultSet.getString("last_name"));
+ }
+ }
+ }
+ }
+ // [END spanner_postgresql_data_boost]
+
+ // [START spanner_partitioned_dml]
+ static void partitionedDml(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Enable Partitioned DML on this connection.
+ connection
+ .createStatement()
+ .execute("SET AUTOCOMMIT_DML_MODE='PARTITIONED_NON_ATOMIC'");
+ // Back-fill a default value for the MarketingBudget column.
+ long lowerBoundUpdateCount =
+ connection
+ .createStatement()
+ .executeUpdate("UPDATE Albums "
+ + "SET MarketingBudget=0 "
+ + "WHERE MarketingBudget IS NULL");
+ System.out.printf("Updated at least %d albums\n", lowerBoundUpdateCount);
+ }
+ }
+ // [END spanner_partitioned_dml]
+
+ // [START spanner_postgresql_partitioned_dml]
+ static void partitionedDmlPostgreSQL(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties) throws SQLException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database),
+ properties)) {
+ // Enable Partitioned DML on this connection.
+ connection
+ .createStatement()
+ .execute("set spanner.autocommit_dml_mode='partitioned_non_atomic'");
+ // Back-fill a default value for the MarketingBudget column.
+ long lowerBoundUpdateCount =
+ connection
+ .createStatement()
+ .executeUpdate("update albums "
+ + "set marketing_budget=0 "
+ + "where marketing_budget is null");
+ System.out.printf("Updated at least %d albums\n", lowerBoundUpdateCount);
+ }
+ }
+ // [END spanner_postgresql_partitioned_dml]
+
+ /** The expected number of command line arguments. */
+ private static final int NUM_EXPECTED_ARGS = 3;
+
+ /**
+ * Main method for running a sample.
+ *
+ * @param args the command line arguments
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != NUM_EXPECTED_ARGS) {
+ printUsageAndExit();
+ }
+ try (DatabaseAdminClient dbAdminClient = createDatabaseAdminClient()) {
+ final String command = args[0];
+ DatabaseId databaseId = DatabaseId.of(
+ SpannerOptions.getDefaultInstance().getProjectId(),
+ args[1],
+ args[2]);
+
+ run(dbAdminClient, command, databaseId);
+ }
+ System.out.println();
+ System.out.println("Finished running sample");
+ }
+
+ static DatabaseAdminClient createDatabaseAdminClient() throws Exception {
+ String emulatorHost = System.getenv("SPANNER_EMULATOR_HOST");
+ if (!Strings.isNullOrEmpty(emulatorHost)) {
+ return DatabaseAdminClient.create(
+ DatabaseAdminSettings.newBuilder()
+ .setTransportChannelProvider(
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setEndpoint(emulatorHost)
+ .setChannelConfigurator(
+ ManagedChannelBuilder::usePlaintext)
+ .build())
+ .setCredentialsProvider(NoCredentialsProvider.create())
+ .build());
+ }
+ return DatabaseAdminClient.create();
+ }
+
+ static Properties createProperties() {
+ Properties properties = new Properties();
+ String emulatorHost = System.getenv("SPANNER_EMULATOR_HOST");
+ if (!Strings.isNullOrEmpty(emulatorHost)) {
+ properties.put("autoConfigEmulator", "true");
+ properties.put("endpoint", emulatorHost);
+ }
+ return properties;
+ }
+
+ static void run(
+ final DatabaseAdminClient dbAdminClient,
+ final String command,
+ final DatabaseId database) throws Exception {
+ if (
+ !runGoogleSQLSample(dbAdminClient, command, database)
+ && !runPostgreSQLSample(dbAdminClient, command, database)) {
+ System.err.println();
+ System.err.println("Unknown command: " + command);
+ System.err.println();
+ printUsageAndExit();
+ }
+ }
+
+ static boolean runGoogleSQLSample(
+ final DatabaseAdminClient dbAdminClient,
+ final String command,
+ final DatabaseId database) throws Exception {
+ switch (command) {
+ case "createdatabase":
+ createDatabase(
+ dbAdminClient,
+ InstanceName.of(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance()),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "writeusingdml":
+ writeDataWithDml(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "writeusingdmlbatch":
+ writeDataWithDmlBatch(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "write":
+ writeDataWithMutations(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "query":
+ queryData(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "querywithparameter":
+ queryWithParameter(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "addmarketingbudget":
+ addColumn(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "ddlbatch":
+ ddlBatch(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "update":
+ updateDataWithMutations(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "querymarketingbudget":
+ queryDataWithNewColumn(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "writewithtransactionusingdml":
+ writeWithTransactionUsingDml(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "tags":
+ tags(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "readonlytransaction":
+ readOnlyTransaction(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "databoost":
+ dataBoost(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "pdml":
+ partitionedDml(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static boolean runPostgreSQLSample(
+ final DatabaseAdminClient dbAdminClient,
+ final String command,
+ final DatabaseId database) throws Exception {
+ switch (command) {
+ case "createpgdatabase":
+ createPostgreSQLDatabase(
+ dbAdminClient,
+ InstanceName.of(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance()),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "writeusingdmlpg":
+ writeDataWithDmlPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "writeusingdmlbatchpg":
+ writeDataWithDmlBatchPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "writepg":
+ writeDataWithMutationsPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "querypg":
+ queryDataPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "querywithparameterpg":
+ queryWithParameterPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "addmarketingbudgetpg":
+ addColumnPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "ddlbatchpg":
+ ddlBatchPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "updatepg":
+ updateDataWithMutationsPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "querymarketingbudgetpg":
+ queryDataWithNewColumnPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "writewithtransactionusingdmlpg":
+ writeWithTransactionUsingDmlPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "tagspg":
+ tagsPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "readonlytransactionpg":
+ readOnlyTransactionPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "databoostpg":
+ dataBoostPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ case "pdmlpg":
+ partitionedDmlPostgreSQL(
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(),
+ database.getDatabase(),
+ createProperties());
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static void printUsageAndExit() {
+ System.err.println("Usage:");
+ System.err.println(" JdbcSample ");
+ System.err.println();
+ System.err.println("Examples:");
+ System.err.println(" JdbcSample createdatabase my-instance example-db");
+ System.exit(1);
+ }
+}
diff --git a/samples/snippets/src/main/java/com/example/spanner/jdbc/package-info.java b/samples/snippets/src/main/java/com/example/spanner/jdbc/package-info.java
new file mode 100644
index 000000000..bdaf4070c
--- /dev/null
+++ b/samples/snippets/src/main/java/com/example/spanner/jdbc/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+/** Sample package for the Spanner JDBC driver. */
+package com.example.spanner.jdbc;
diff --git a/samples/snippets/src/test/java/com/example/spanner/jdbc/JdbcSampleTest.java b/samples/snippets/src/test/java/com/example/spanner/jdbc/JdbcSampleTest.java
new file mode 100644
index 000000000..9365ad9bf
--- /dev/null
+++ b/samples/snippets/src/test/java/com/example/spanner/jdbc/JdbcSampleTest.java
@@ -0,0 +1,396 @@
+/*
+ * 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.example.spanner.jdbc;
+
+import static com.example.spanner.jdbc.JdbcSample.addColumn;
+import static com.example.spanner.jdbc.JdbcSample.addColumnPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.createConnection;
+import static com.example.spanner.jdbc.JdbcSample.createConnectionWithEmulator;
+import static com.example.spanner.jdbc.JdbcSample.createDatabase;
+import static com.example.spanner.jdbc.JdbcSample.createPostgreSQLDatabase;
+import static com.example.spanner.jdbc.JdbcSample.dataBoost;
+import static com.example.spanner.jdbc.JdbcSample.dataBoostPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.ddlBatch;
+import static com.example.spanner.jdbc.JdbcSample.ddlBatchPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.partitionedDml;
+import static com.example.spanner.jdbc.JdbcSample.partitionedDmlPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.queryData;
+import static com.example.spanner.jdbc.JdbcSample.queryDataPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.queryDataWithNewColumn;
+import static com.example.spanner.jdbc.JdbcSample.queryDataWithNewColumnPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.queryWithParameter;
+import static com.example.spanner.jdbc.JdbcSample.queryWithParameterPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.readOnlyTransaction;
+import static com.example.spanner.jdbc.JdbcSample.readOnlyTransactionPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.tags;
+import static com.example.spanner.jdbc.JdbcSample.tagsPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.updateDataWithMutations;
+import static com.example.spanner.jdbc.JdbcSample.updateDataWithMutationsPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.writeDataWithDml;
+import static com.example.spanner.jdbc.JdbcSample.writeDataWithDmlBatch;
+import static com.example.spanner.jdbc.JdbcSample.writeDataWithDmlBatchPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.writeDataWithDmlPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.writeDataWithMutations;
+import static com.example.spanner.jdbc.JdbcSample.writeDataWithMutationsPostgreSQL;
+import static com.example.spanner.jdbc.JdbcSample.writeWithTransactionUsingDml;
+import static com.example.spanner.jdbc.JdbcSample.writeWithTransactionUsingDmlPostgreSQL;
+import static org.junit.Assert.assertEquals;
+
+import com.google.api.gax.core.NoCredentialsProvider;
+import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminSettings;
+import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
+import com.google.cloud.spanner.admin.instance.v1.InstanceAdminSettings;
+import com.google.cloud.spanner.connection.SpannerPool;
+import com.google.common.collect.ImmutableList;
+import com.google.spanner.admin.instance.v1.Instance;
+import com.google.spanner.admin.instance.v1.InstanceConfig;
+import com.google.spanner.admin.instance.v1.InstanceName;
+import com.google.spanner.admin.instance.v1.ProjectName;
+import com.google.spanner.v1.DatabaseName;
+import io.grpc.ManagedChannelBuilder;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Properties;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+
+@RunWith(JUnit4.class)
+public class JdbcSampleTest {
+ private static final String PROJECT_ID = "emulator-project";
+ private static final String INSTANCE_ID = "test-instance";
+ private static final String DATABASE_ID = "test-database";
+ private static final String PG_DATABASE_ID = "pg-test-database";
+
+ private static final ProjectName PROJECT_NAME = ProjectName.of(PROJECT_ID);
+
+ private static final InstanceName INSTANCE_NAME = InstanceName.of(PROJECT_ID, INSTANCE_ID);
+
+ private static GenericContainer> emulator;
+
+ private static Properties properties;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ emulator =
+ new GenericContainer<>(
+ DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:latest"))
+ // .withExposedPorts(9010)
+ .waitingFor(Wait.forListeningPort());
+ // TODO: Remove and replace with dynamic port binding when Spanner client library 6.64.0 has
+ // been released.
+ emulator.setPortBindings(ImmutableList.of("9010:9010"));
+ emulator.start();
+ try (InstanceAdminClient client =
+ InstanceAdminClient.create(
+ InstanceAdminSettings.newBuilder()
+ .setTransportChannelProvider(
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setEndpoint(emulator.getHost() + ":" + emulator.getMappedPort(9010))
+ .setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
+ .build())
+ .setCredentialsProvider(NoCredentialsProvider.create())
+ .build())) {
+ InstanceConfig config =
+ client.listInstanceConfigs(PROJECT_NAME).iterateAll().iterator().next();
+ client
+ .createInstanceAsync(
+ PROJECT_NAME,
+ INSTANCE_ID,
+ Instance.newBuilder()
+ .setConfig(config.getName())
+ .setDisplayName("Test Instance")
+ .setNodeCount(1)
+ .build())
+ .get();
+ }
+ // Create properties for the JDBC driver to connect to the emulator.
+ properties = new Properties();
+ properties.put("autoConfigEmulator", "true");
+ properties.put("lenient", "true");
+ properties.put("endpoint", emulator.getHost() + ":" + emulator.getMappedPort(9010));
+ }
+
+ @AfterClass
+ public static void cleanup() {
+ SpannerPool.closeSpannerPool();
+ emulator.stop();
+ }
+
+ DatabaseAdminClient createDatabaseAdminClient() throws Exception {
+ return DatabaseAdminClient.create(
+ DatabaseAdminSettings.newBuilder()
+ .setTransportChannelProvider(
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setEndpoint(emulator.getHost() + ":" + emulator.getMappedPort(9010))
+ .setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
+ .build())
+ .setCredentialsProvider(NoCredentialsProvider.create())
+ .build());
+ }
+
+ @Test
+ public void testGoogleSQLSamples() throws Exception {
+ String result;
+ try (DatabaseAdminClient client = createDatabaseAdminClient()) {
+ result = runSample(() -> createDatabase(client, INSTANCE_NAME, DATABASE_ID, properties));
+ }
+ assertEquals(
+ "Created database [" + DatabaseName.of(PROJECT_ID, INSTANCE_ID, DATABASE_ID) + "]\n",
+ result);
+
+ result = runSample(() -> createConnection(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("Hello World!\n", result);
+
+ result = runSample(() -> createConnectionWithEmulator(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("Hello World!\n", result);
+
+ result = runSample(() -> writeDataWithDml(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("4 records inserted.\n", result);
+
+ result =
+ runSample(() -> writeDataWithDmlBatch(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("3 records inserted.\n", result);
+
+ result =
+ runSample(() -> writeDataWithMutations(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("Inserted 10 rows.\n", result);
+
+ result = runSample(() -> queryData(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals(
+ "1 2 Go, Go, Go\n"
+ + "2 2 Forever Hold Your Peace\n"
+ + "1 1 Total Junk\n"
+ + "2 1 Green\n"
+ + "2 3 Terrified\n",
+ result);
+
+ result = runSample(() -> queryWithParameter(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("12 Melissa Garcia\n", result);
+
+ result = runSample(() -> addColumn(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("Added MarketingBudget column\n", result);
+
+ result = runSample(() -> ddlBatch(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("Added Venues and Concerts tables\n", result);
+
+ result =
+ runSample(() -> updateDataWithMutations(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("Updated albums\n", result);
+
+ result =
+ runSample(() -> queryDataWithNewColumn(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals(
+ "1 2 null\n" + "2 2 500000\n" + "1 1 100000\n" + "2 1 null\n" + "2 3 null\n", result);
+
+ result =
+ runSample(
+ () -> writeWithTransactionUsingDml(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("Transferred marketing budget from Album 2 to Album 1\n", result);
+
+ result =
+ runSample(
+ () -> tags(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("Reduced marketing budget\n", result);
+
+ result = runSample(() -> readOnlyTransaction(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals(
+ "1 1 Total Junk\n"
+ + "1 2 Go, Go, Go\n"
+ + "2 1 Green\n"
+ + "2 2 Forever Hold Your Peace\n"
+ + "2 3 Terrified\n"
+ + "2 2 Forever Hold Your Peace\n"
+ + "1 2 Go, Go, Go\n"
+ + "2 1 Green\n"
+ + "2 3 Terrified\n"
+ + "1 1 Total Junk\n",
+ result);
+
+ result = runSample(() -> dataBoost(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals(
+ "2 Catalina Smith\n"
+ + "4 Lea Martin\n"
+ + "12 Melissa Garcia\n"
+ + "14 Jacqueline Long\n"
+ + "16 Sarah Wilson\n"
+ + "18 Maya Patel\n"
+ + "1 Marc Richards\n"
+ + "3 Alice Trentor\n"
+ + "5 David Lomond\n"
+ + "13 Russel Morales\n"
+ + "15 Dylan Shaw\n"
+ + "17 Ethan Miller\n",
+ result);
+
+ result = runSample(() -> partitionedDml(PROJECT_ID, INSTANCE_ID, DATABASE_ID, properties));
+ assertEquals("Updated at least 3 albums\n", result);
+ }
+
+ @Test
+ public void testPostgreSQLSamples() throws Exception {
+ String result;
+ try (DatabaseAdminClient client = createDatabaseAdminClient()) {
+ result =
+ runSample(
+ () -> createPostgreSQLDatabase(client, INSTANCE_NAME, PG_DATABASE_ID, properties));
+ }
+ assertEquals(
+ "Created database [" + DatabaseName.of(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID) + "]\n",
+ result);
+
+ result = runSample(() -> createConnection(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("Hello World!\n", result);
+
+ result = runSample(() -> createConnectionWithEmulator(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("Hello World!\n", result);
+
+ result =
+ runSample(
+ () -> writeDataWithDmlPostgreSQL(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("4 records inserted.\n", result);
+
+ result =
+ runSample(
+ () ->
+ writeDataWithDmlBatchPostgreSQL(
+ PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("3 records inserted.\n", result);
+
+ result =
+ runSample(
+ () ->
+ writeDataWithMutationsPostgreSQL(
+ PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("Inserted 10 rows.\n", result);
+
+ result =
+ runSample(() -> queryDataPostgreSQL(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals(
+ "1 2 Go, Go, Go\n"
+ + "2 2 Forever Hold Your Peace\n"
+ + "1 1 Total Junk\n"
+ + "2 1 Green\n"
+ + "2 3 Terrified\n",
+ result);
+
+ result =
+ runSample(
+ () ->
+ queryWithParameterPostgreSQL(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("12 Melissa Garcia\n", result);
+
+ result =
+ runSample(() -> addColumnPostgreSQL(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("Added marketing_budget column\n", result);
+
+ result =
+ runSample(() -> ddlBatchPostgreSQL(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("Added venues and concerts tables\n", result);
+
+ result =
+ runSample(
+ () ->
+ updateDataWithMutationsPostgreSQL(
+ PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("Updated albums\n", result);
+
+ result =
+ runSample(
+ () ->
+ queryDataWithNewColumnPostgreSQL(
+ PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals(
+ "1 2 null\n" + "2 2 500000\n" + "1 1 100000\n" + "2 1 null\n" + "2 3 null\n", result);
+
+ result =
+ runSample(
+ () ->
+ writeWithTransactionUsingDmlPostgreSQL(
+ PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("Transferred marketing budget from Album 2 to Album 1\n", result);
+
+ result =
+ runSample(
+ () ->
+ tagsPostgreSQL(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("Reduced marketing budget\n", result);
+
+ result =
+ runSample(
+ () ->
+ readOnlyTransactionPostgreSQL(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals(
+ "1 1 Total Junk\n"
+ + "1 2 Go, Go, Go\n"
+ + "2 1 Green\n"
+ + "2 2 Forever Hold Your Peace\n"
+ + "2 3 Terrified\n"
+ + "2 2 Forever Hold Your Peace\n"
+ + "1 2 Go, Go, Go\n"
+ + "2 1 Green\n"
+ + "2 3 Terrified\n"
+ + "1 1 Total Junk\n",
+ result);
+
+ result =
+ runSample(() -> dataBoostPostgreSQL(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals(
+ "2 Catalina Smith\n"
+ + "4 Lea Martin\n"
+ + "12 Melissa Garcia\n"
+ + "14 Jacqueline Long\n"
+ + "16 Sarah Wilson\n"
+ + "18 Maya Patel\n"
+ + "1 Marc Richards\n"
+ + "3 Alice Trentor\n"
+ + "5 David Lomond\n"
+ + "13 Russel Morales\n"
+ + "15 Dylan Shaw\n"
+ + "17 Ethan Miller\n",
+ result);
+
+ result =
+ runSample(
+ () -> partitionedDmlPostgreSQL(PROJECT_ID, INSTANCE_ID, PG_DATABASE_ID, properties));
+ assertEquals("Updated at least 3 albums\n", result);
+ }
+
+ interface Sample {
+ void run() throws Exception;
+ }
+
+ String runSample(Sample sample) throws Exception {
+ PrintStream stdOut = System.out;
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(bout);
+ System.setOut(out);
+ try {
+ sample.run();
+ } finally {
+ System.setOut(stdOut);
+ }
+ return bout.toString();
+ }
+}
From 2860069896e47810d815647b3a7abc19800ef4dd Mon Sep 17 00:00:00 2001
From: Hengfeng Li
Date: Wed, 10 Apr 2024 23:20:08 +1000
Subject: [PATCH 16/29] chore: add OpenTelemetry metric for client lib
latencies (#1559)
---
pom.xml | 11 ++++
.../spanner/jdbc/AbstractJdbcStatement.java | 5 ++
.../cloud/spanner/jdbc/JdbcConnection.java | 31 +++++++++++
.../google/cloud/spanner/jdbc/Metrics.java | 53 +++++++++++++++++++
.../JdbcConnectionGeneratedSqlScriptTest.java | 2 +
.../spanner/jdbc/JdbcConnectionTest.java | 7 ++-
6 files changed, 108 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/com/google/cloud/spanner/jdbc/Metrics.java
diff --git a/pom.xml b/pom.xml
index 38d49832b..2e9478636 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,6 +66,13 @@
pom
import
+
+ io.opentelemetry
+ opentelemetry-bom
+ 1.36.0
+ pom
+ import
+
com.google.cloud
google-cloud-shared-dependencies
@@ -152,6 +159,10 @@
com.google.api.grpc
proto-google-cloud-spanner-v1
+
+ io.opentelemetry
+ opentelemetry-api
+
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java
index 97131724d..61c272fb7 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java
@@ -24,11 +24,13 @@
import com.google.cloud.spanner.connection.Connection;
import com.google.cloud.spanner.connection.StatementResult;
import com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType;
+import com.google.common.base.Stopwatch;
import com.google.rpc.Code;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
+import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@@ -227,7 +229,10 @@ private T doWithStatementTimeout(
StatementTimeout originalTimeout = setTemporaryStatementTimeout();
T result = null;
try {
+ Stopwatch stopwatch = Stopwatch.createStarted();
result = runnable.get();
+ Duration executionDuration = stopwatch.elapsed();
+ connection.recordClientLibLatencyMetric(executionDuration.toMillis());
return result;
} catch (SpannerException spannerException) {
throw JdbcSqlExceptionFactory.of(spannerException);
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcConnection.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcConnection.java
index 2c7d6cc00..bafdc6b09 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcConnection.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcConnection.java
@@ -21,17 +21,23 @@
import com.google.api.client.util.Preconditions;
import com.google.cloud.spanner.CommitResponse;
+import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Mutation;
import com.google.cloud.spanner.SpannerException;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.TimestampBound;
import com.google.cloud.spanner.connection.AutocommitDmlMode;
import com.google.cloud.spanner.connection.Connection;
import com.google.cloud.spanner.connection.ConnectionOptions;
import com.google.cloud.spanner.connection.SavepointSupport;
import com.google.cloud.spanner.connection.TransactionMode;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.common.AttributesBuilder;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
@@ -80,9 +86,21 @@ class JdbcConnection extends AbstractJdbcConnection {
private final boolean useLegacyIsValidCheck;
+ private final Metrics metrics;
+
+ private final Attributes metricAttributes;
+
JdbcConnection(String connectionUrl, ConnectionOptions options) throws SQLException {
super(connectionUrl, options);
this.useLegacyIsValidCheck = useLegacyValidCheck();
+ OpenTelemetry openTelemetry;
+ if (SpannerOptions.isEnabledOpenTelemetryMetrics()) {
+ openTelemetry = this.getSpanner().getOptions().getOpenTelemetry();
+ } else {
+ openTelemetry = OpenTelemetry.noop();
+ }
+ this.metrics = new Metrics(openTelemetry);
+ this.metricAttributes = createMetricAttributes(this.getConnectionOptions().getDatabaseId());
}
static boolean useLegacyValidCheck() {
@@ -96,6 +114,19 @@ static boolean useLegacyValidCheck() {
return false;
}
+ @VisibleForTesting
+ static Attributes createMetricAttributes(DatabaseId databaseId) {
+ AttributesBuilder attributesBuilder = Attributes.builder();
+ attributesBuilder.put("database", databaseId.getDatabase());
+ attributesBuilder.put("instance_id", databaseId.getInstanceId().getInstance());
+ attributesBuilder.put("project_id", databaseId.getInstanceId().getProject());
+ return attributesBuilder.build();
+ }
+
+ public void recordClientLibLatencyMetric(long value) {
+ metrics.recordClientLibLatency(value, metricAttributes);
+ }
+
@Override
public Statement createStatement() throws SQLException {
checkClosed();
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/Metrics.java b/src/main/java/com/google/cloud/spanner/jdbc/Metrics.java
new file mode 100644
index 000000000..77f238466
--- /dev/null
+++ b/src/main/java/com/google/cloud/spanner/jdbc/Metrics.java
@@ -0,0 +1,53 @@
+/*
+ * 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 io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.metrics.LongHistogram;
+import io.opentelemetry.api.metrics.Meter;
+import java.util.Arrays;
+import java.util.List;
+
+class Metrics {
+ static final String INSTRUMENTATION_SCOPE = "cloud.google.com/java";
+ static final String SPANNER_CLIENT_LIB_LATENCY = "spanner/jdbc/client_lib_latencies";
+ static final String SPANNER_CLIENT_LIB_LATENCY_DESCRIPTION =
+ "Latency when the client library receives a call and returns a response";
+
+ private final LongHistogram spannerClientLibLatencies;
+
+ Metrics(OpenTelemetry openTelemetry) {
+ Meter meter = openTelemetry.getMeter(INSTRUMENTATION_SCOPE);
+ List RPC_MILLIS_BUCKET_BOUNDARIES =
+ Arrays.asList(
+ 1L, 2L, 3L, 4L, 5L, 6L, 8L, 10L, 13L, 16L, 20L, 25L, 30L, 40L, 50L, 65L, 80L, 100L,
+ 130L, 160L, 200L, 250L, 300L, 400L, 500L, 650L, 800L, 1000L, 2000L, 5000L, 10000L,
+ 20000L, 50000L, 100000L);
+ spannerClientLibLatencies =
+ meter
+ .histogramBuilder(SPANNER_CLIENT_LIB_LATENCY)
+ .ofLongs()
+ .setDescription(SPANNER_CLIENT_LIB_LATENCY_DESCRIPTION)
+ .setUnit("ms")
+ .setExplicitBucketBoundariesAdvice(RPC_MILLIS_BUCKET_BOUNDARIES)
+ .build();
+ }
+
+ void recordClientLibLatency(long value, Attributes attributes) {
+ spannerClientLibLatencies.record(value, attributes);
+ }
+}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcConnectionGeneratedSqlScriptTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcConnectionGeneratedSqlScriptTest.java
index 5844feefb..a5d350501 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcConnectionGeneratedSqlScriptTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcConnectionGeneratedSqlScriptTest.java
@@ -19,6 +19,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.connection.AbstractConnectionImplTest;
@@ -64,6 +65,7 @@ public GenericConnection getConnection() {
ConnectionImplTest.createConnection(options, dialect);
when(spannerConnection.getDialect()).thenReturn(dialect);
when(options.getConnection()).thenReturn(spannerConnection);
+ when(options.getDatabaseId()).thenReturn(DatabaseId.of("project", "instance", "database"));
try {
JdbcConnection connection =
new JdbcConnection(
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcConnectionTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcConnectionTest.java
index 8d26bb317..96ea74381 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcConnectionTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcConnectionTest.java
@@ -23,10 +23,12 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.ErrorCode;
import com.google.cloud.spanner.ResultSets;
@@ -80,13 +82,16 @@ private JdbcConnection createConnection(ConnectionOptions options) throws SQLExc
ConnectionImplTest.createConnection(options, dialect);
when(spannerConnection.getDialect()).thenReturn(dialect);
when(options.getConnection()).thenReturn(spannerConnection);
+ when(options.getDatabaseId()).thenReturn(DatabaseId.of("project", "instance", "database"));
return new JdbcConnection(
"jdbc:cloudspanner://localhost/projects/project/instances/instance/databases/database;credentialsUrl=url",
options);
}
private ConnectionOptions mockOptions() {
- return mock(ConnectionOptions.class);
+ ConnectionOptions options = mock(ConnectionOptions.class);
+ when(options.getDatabaseId()).thenReturn(DatabaseId.of("project", "instance", "database"));
+ return options;
}
@Test
From afcbe5ea5701a799729543c9564759570f05feb8 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Wed, 10 Apr 2024 15:44:18 +0200
Subject: [PATCH 17/29] deps: update dependency
com.google.cloud:sdk-platform-java-config to v3.28.1 (#1560)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.cloud:sdk-platform-java-config](https://togithub.com/googleapis/java-shared-config) | `3.27.0` -> `3.28.1` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) |
---
### Configuration
π
**Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).
π¦ **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
β» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
π **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] If you want to rebase/retry this PR, check this box
---
This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/googleapis/java-spanner-jdbc).
---
samples/snippets/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 047c0c761..5928ec6ab 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -9,7 +9,7 @@
com.google.cloud
sdk-platform-java-config
- 3.27.0
+ 3.28.1
From dbbcca342a83476b1f942aab23f21469cf6c8304 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Fri, 12 Apr 2024 15:26:51 +0200
Subject: [PATCH 18/29] deps: update dependency
org.springframework.data:spring-data-bom to v2023.1.5 (#1564)
---
samples/spring-data-jdbc/pom.xml | 2 +-
samples/spring-data-mybatis/pom.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/samples/spring-data-jdbc/pom.xml b/samples/spring-data-jdbc/pom.xml
index c9fbf898d..c1dc7cc00 100644
--- a/samples/spring-data-jdbc/pom.xml
+++ b/samples/spring-data-jdbc/pom.xml
@@ -23,7 +23,7 @@
org.springframework.data
spring-data-bom
- 2023.1.4
+ 2023.1.5
import
pom
diff --git a/samples/spring-data-mybatis/pom.xml b/samples/spring-data-mybatis/pom.xml
index 16e8c3d37..ef9d1d89e 100644
--- a/samples/spring-data-mybatis/pom.xml
+++ b/samples/spring-data-mybatis/pom.xml
@@ -28,7 +28,7 @@
org.springframework.data
spring-data-bom
- 2023.1.4
+ 2023.1.5
import
pom
From 01d4de1df21144e9c3bcf0b4e5192b12cd19dc82 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Fri, 12 Apr 2024 15:27:09 +0200
Subject: [PATCH 19/29] deps: update actions/setup-java action to v4 (#1563)
---
.github/workflows/sample-tests.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/sample-tests.yml b/.github/workflows/sample-tests.yml
index cec19c7a2..7e464ef17 100644
--- a/.github/workflows/sample-tests.yml
+++ b/.github/workflows/sample-tests.yml
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- - uses: actions/setup-java@v3
+ - uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 8
From 6053d79816546130eca7a7016dc9299c079e411f Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Sun, 14 Apr 2024 15:28:01 +0200
Subject: [PATCH 20/29] deps: update actions/checkout action to v4 (#1561)
---
.github/workflows/sample-tests.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/sample-tests.yml b/.github/workflows/sample-tests.yml
index 7e464ef17..eae9314cf 100644
--- a/.github/workflows/sample-tests.yml
+++ b/.github/workflows/sample-tests.yml
@@ -20,7 +20,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
From a71c4f5b3171ab7ec65c4d5fd2de4a3bc0b2bb9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?=
Date: Tue, 16 Apr 2024 08:07:25 +0200
Subject: [PATCH 21/29] chore: make connection property all lower-case (#1566)
---
.../src/main/java/com/example/spanner/jdbc/JdbcSample.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java b/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java
index 2e46058de..b17c6b4d8 100644
--- a/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/jdbc/JdbcSample.java
@@ -1235,7 +1235,7 @@ static void tagsPostgreSQL(
// for the current transaction.
connection
.createStatement()
- .execute("set spanner.transaction_TAG='example-tx-tag'");
+ .execute("set spanner.transaction_tag='example-tx-tag'");
// Set the STATEMENT_TAG session variable to set the request tag
// that should be included with the next SQL statement.
From 2258ae3331a7e89036a202f243b9284108301fc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?=
Date: Tue, 16 Apr 2024 10:44:09 +0200
Subject: [PATCH 22/29] fix: release ResultSet on Statement#close() (#1567)
* fix: release ResultSet on Statement#close()
Release the underlying resources of any ResultSet when Statement#close()
is called. This is necessary in case someone executes a query in
auto-commit mode using Statement#execute(String), and never reads and/or
closes the result that was returned.
* fix: close result set when Statement is closed
---
.../spanner/jdbc/AbstractJdbcStatement.java | 2 +-
.../cloud/spanner/jdbc/JdbcStatement.java | 19 +++-
.../spanner/jdbc/StatementResourcesTest.java | 88 +++++++++++++++++++
3 files changed, 105 insertions(+), 4 deletions(-)
create mode 100644 src/test/java/com/google/cloud/spanner/jdbc/StatementResourcesTest.java
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java
index 61c272fb7..d12678bf2 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java
@@ -359,7 +359,7 @@ public void cancel() throws SQLException {
}
@Override
- public void close() {
+ public void close() throws SQLException {
this.closed = true;
}
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java
index 1db0c4070..19b325654 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java
@@ -62,6 +62,12 @@ enum BatchType {
super(connection);
}
+ @Override
+ public void close() throws SQLException {
+ setCurrentResultSet(null);
+ super.close();
+ }
+
@Override
public ResultSet executeQuery(String sql) throws SQLException {
checkClosed();
@@ -267,7 +273,7 @@ boolean executeStatement(Statement statement, ImmutableList generatedKey
// keys. We can safely use '==', as the addReturningToStatement(..) method returns the same
// instance if no generated keys were requested.
if (statementWithReturning == statement) {
- currentResultSet = JdbcResultSet.of(this, result.getResultSet());
+ setCurrentResultSet(JdbcResultSet.of(this, result.getResultSet()));
currentUpdateCount = JdbcConstants.STATEMENT_RESULT_SET;
return true;
}
@@ -275,11 +281,11 @@ boolean executeStatement(Statement statement, ImmutableList generatedKey
this.currentUpdateCount = extractUpdateCountAndClose(result.getResultSet());
return false;
case UPDATE_COUNT:
- currentResultSet = null;
+ setCurrentResultSet(null);
currentUpdateCount = result.getUpdateCount();
return false;
case NO_RESULT:
- currentResultSet = null;
+ setCurrentResultSet(null);
currentUpdateCount = JdbcConstants.STATEMENT_NO_RESULT;
return false;
default:
@@ -294,6 +300,13 @@ public ResultSet getResultSet() throws SQLException {
return currentResultSet;
}
+ void setCurrentResultSet(ResultSet resultSet) throws SQLException {
+ if (this.currentResultSet != null) {
+ this.currentResultSet.close();
+ }
+ this.currentResultSet = resultSet;
+ }
+
/**
* Returns the update count of the last update statement. Will return {@link
* JdbcConstants#STATEMENT_RESULT_SET} if the last statement returned a {@link ResultSet} and will
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/StatementResourcesTest.java b/src/test/java/com/google/cloud/spanner/jdbc/StatementResourcesTest.java
new file mode 100644
index 000000000..ea1b313ac
--- /dev/null
+++ b/src/test/java/com/google/cloud/spanner/jdbc/StatementResourcesTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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 com.google.cloud.spanner.connection.AbstractMockServerTest;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class StatementResourcesTest extends AbstractMockServerTest {
+ private static final int MIN_SESSIONS = 1;
+ private static final int MAX_SESSIONS = 2;
+
+ private String createUrl() {
+ return String.format(
+ "jdbc:cloudspanner://localhost:%d/projects/%s/instances/%s/databases/%s"
+ + "?usePlainText=true;minSessions=%d;maxSessions=%d",
+ getPort(), "proj", "inst", "db", MIN_SESSIONS, MAX_SESSIONS);
+ }
+
+ @Test
+ public void testMultipleQueriesOnOneStatement() throws SQLException {
+ try (Connection connection = DriverManager.getConnection(createUrl())) {
+ try (Statement statement = connection.createStatement()) {
+ for (int i = 0; i < MAX_SESSIONS + 1; i++) {
+ // Execute a query without reading or closing the result.
+ statement.execute("SELECT 1");
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testMultipleStatementsWithOneQuery() throws SQLException {
+ try (Connection connection = DriverManager.getConnection(createUrl())) {
+ for (int i = 0; i < MAX_SESSIONS + 1; i++) {
+ try (Statement statement = connection.createStatement()) {
+ // Execute a query without reading or closing the result.
+ statement.execute("SELECT 1");
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testMultipleQueriesOnOnePreparedStatement() throws SQLException {
+ try (Connection connection = DriverManager.getConnection(createUrl())) {
+ try (PreparedStatement statement = connection.prepareStatement("SELECT 1")) {
+ for (int i = 0; i < MAX_SESSIONS + 1; i++) {
+ // Execute a query without reading or closing the result.
+ statement.execute();
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testMultiplePreparedStatementsWithOneQuery() throws SQLException {
+ try (Connection connection = DriverManager.getConnection(createUrl())) {
+ for (int i = 0; i < MAX_SESSIONS + 1; i++) {
+ try (PreparedStatement statement = connection.prepareStatement("SELECT 1")) {
+ // Execute a query without reading or closing the result.
+ statement.execute();
+ }
+ }
+ }
+ }
+}
From b57662fb65b74b329103ef63265192d7026b2c2d Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Tue, 16 Apr 2024 13:04:10 +0200
Subject: [PATCH 23/29] deps: update dependency
com.google.cloud:google-cloud-spanner-bom to v6.64.0 (#1565)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.cloud:google-cloud-spanner-bom](https://togithub.com/googleapis/java-spanner) | `6.63.0` -> `6.64.0` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) |
---
### Release Notes
googleapis/java-spanner (com.google.cloud:google-cloud-spanner-bom)
### [`v6.64.0`](https://togithub.com/googleapis/java-spanner/blob/HEAD/CHANGELOG.md#6640-2024-04-12)
[Compare Source](https://togithub.com/googleapis/java-spanner/compare/v6.63.0...v6.64.0)
##### Features
- Add endpoint connection URL property ([#2969](https://togithub.com/googleapis/java-spanner/issues/2969)) ([c9be29c](https://togithub.com/googleapis/java-spanner/commit/c9be29c717924d7f4c5acd8fe09ee371d0101642))
- Add PG OID support ([#2736](https://togithub.com/googleapis/java-spanner/issues/2736)) ([ba2a4af](https://togithub.com/googleapis/java-spanner/commit/ba2a4afa5c1d64c932e9687d52b15c28d9dd7d91))
- Add SessionPoolOptions, SpannerOptions protos in executor protos ([#2932](https://togithub.com/googleapis/java-spanner/issues/2932)) ([1673fd7](https://togithub.com/googleapis/java-spanner/commit/1673fd70df4ebfaa4b5fa07112d152119427699a))
- Support max_commit_delay in Connection API ([#2954](https://togithub.com/googleapis/java-spanner/issues/2954)) ([a8f1852](https://togithub.com/googleapis/java-spanner/commit/a8f185261c812e7d6c92cb61ecc1f9c78ba3c4d9))
##### Bug Fixes
- Executor framework changes skipped in clirr checks, and added exception for partition methods in admin class ([#3000](https://togithub.com/googleapis/java-spanner/issues/3000)) ([c2d8e95](https://togithub.com/googleapis/java-spanner/commit/c2d8e955abddb0117f1b3b94c2d9650d2cf4fdfd))
##### Dependencies
- Update actions/checkout action to v4 ([#3006](https://togithub.com/googleapis/java-spanner/issues/3006)) ([368a9f3](https://togithub.com/googleapis/java-spanner/commit/368a9f33758961d8e3fd387ec94d380e7c6460cc))
- Update actions/github-script action to v7 ([#3007](https://togithub.com/googleapis/java-spanner/issues/3007)) ([b0cfea6](https://togithub.com/googleapis/java-spanner/commit/b0cfea6e73b7293f564357e8d1c8c6bb2e0cf855))
- Update actions/setup-java action to v4 ([#3008](https://togithub.com/googleapis/java-spanner/issues/3008)) ([d337080](https://togithub.com/googleapis/java-spanner/commit/d337080089dbd58cb4bf94f2cb5925f627435d39))
- Update dependency com.google.cloud:google-cloud-monitoring to v3.42.0 ([#2997](https://togithub.com/googleapis/java-spanner/issues/2997)) ([0615beb](https://togithub.com/googleapis/java-spanner/commit/0615beb806ef62dbbfcc6bbffd082adc9c62372c))
- Update dependency com.google.cloud:google-cloud-trace to v2.41.0 ([#2998](https://togithub.com/googleapis/java-spanner/issues/2998)) ([f50cd04](https://togithub.com/googleapis/java-spanner/commit/f50cd04660f480c62ddbd6c8a9e892cd95ec16b0))
- Update dependency commons-io:commons-io to v2.16.1 ([#3020](https://togithub.com/googleapis/java-spanner/issues/3020)) ([aafd5b9](https://togithub.com/googleapis/java-spanner/commit/aafd5b9514c14a0dbfd0bf2616990f3c347ac0c6))
- Update opentelemetry.version to v1.37.0 ([#3021](https://togithub.com/googleapis/java-spanner/issues/3021)) ([8f1ed2a](https://togithub.com/googleapis/java-spanner/commit/8f1ed2ac20896fb413749bb18652764096f1fb2d))
- Update stcarolas/setup-maven action to v5 ([#3009](https://togithub.com/googleapis/java-spanner/issues/3009)) ([541acd2](https://togithub.com/googleapis/java-spanner/commit/541acd23aaf2c9336615406e30618fb65606e6c5))
---
### Configuration
π
**Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).
π¦ **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
β» **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
π **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] If you want to rebase/retry this PR, check this box
---
This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/googleapis/java-spanner-jdbc).
---
pom.xml | 2 +-
.../spanner/jdbc/AbstractJdbcWrapper.java | 8 ++--
.../cloud/spanner/jdbc/JdbcResultSet.java | 43 +++++++++++--------
.../cloud/spanner/jdbc/JdbcTypeConverter.java | 16 +++++++
.../spanner/jdbc/AllTypesMockServerTest.java | 34 +++++++++++++--
.../jdbc/PartitionedQueryMockServerTest.java | 3 +-
.../spanner/jdbc/RandomResultSetTest.java | 16 +++++--
7 files changed, 93 insertions(+), 29 deletions(-)
diff --git a/pom.xml b/pom.xml
index 2e9478636..f4a9fc855 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,7 +62,7 @@
com.google.cloud
google-cloud-spanner-bom
- 6.63.0
+ 6.64.0
pom
import
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcWrapper.java b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcWrapper.java
index b06e5999e..aa65ee6f9 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcWrapper.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcWrapper.java
@@ -16,6 +16,8 @@
package com.google.cloud.spanner.jdbc;
+import static com.google.cloud.spanner.jdbc.JdbcTypeConverter.getMainTypeCode;
+
import com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.Type;
import com.google.cloud.spanner.Type.Code;
@@ -42,7 +44,7 @@ abstract class AbstractJdbcWrapper implements Wrapper {
*/
static int extractColumnType(Type type) {
Preconditions.checkNotNull(type);
- switch (type.getCode()) {
+ switch (getMainTypeCode(type)) {
case BOOL:
return Types.BOOLEAN;
case BYTES:
@@ -139,7 +141,7 @@ static String getClassName(int sqlType) {
*/
static String getClassName(Type type) {
Preconditions.checkNotNull(type);
- switch (type.getCode()) {
+ switch (getMainTypeCode(type)) {
case BOOL:
return Boolean.class.getName();
case BYTES:
@@ -162,7 +164,7 @@ static String getClassName(Type type) {
case TIMESTAMP:
return Timestamp.class.getName();
case ARRAY:
- switch (type.getArrayElementType().getCode()) {
+ switch (getMainTypeCode(type.getArrayElementType())) {
case BOOL:
return Boolean[].class.getName();
case BYTES:
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java
index 91ec976b8..dcb259857 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java
@@ -16,6 +16,8 @@
package com.google.cloud.spanner.jdbc;
+import static com.google.cloud.spanner.jdbc.JdbcTypeConverter.getMainTypeCode;
+
import com.google.cloud.spanner.ResultSets;
import com.google.cloud.spanner.Struct;
import com.google.cloud.spanner.Type;
@@ -195,7 +197,7 @@ public String getString(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case BOOL:
return isNull ? null : String.valueOf(spanner.getBoolean(spannerIndex));
@@ -233,7 +235,7 @@ public boolean getBoolean(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case BOOL:
return !isNull && spanner.getBoolean(spannerIndex);
@@ -266,7 +268,7 @@ public byte getByte(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case BOOL:
return isNull ? (byte) 0 : (spanner.getBoolean(spannerIndex) ? (byte) 1 : 0);
@@ -305,7 +307,7 @@ public short getShort(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case BOOL:
return isNull ? 0 : (spanner.getBoolean(spannerIndex) ? (short) 1 : 0);
@@ -344,7 +346,7 @@ public int getInt(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case BOOL:
return isNull ? 0 : (spanner.getBoolean(spannerIndex) ? 1 : 0);
@@ -383,7 +385,7 @@ public long getLong(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case BOOL:
return isNull ? 0L : (spanner.getBoolean(spannerIndex) ? 1L : 0L);
@@ -418,7 +420,7 @@ public float getFloat(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case BOOL:
return isNull ? 0 : (spanner.getBoolean(spannerIndex) ? (float) 1 : 0);
@@ -451,7 +453,7 @@ public double getDouble(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case BOOL:
return isNull ? 0 : (spanner.getBoolean(spannerIndex) ? (double) 1 : 0);
@@ -492,7 +494,7 @@ public Date getDate(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case DATE:
return isNull ? null : JdbcTypeConverter.toSqlDate(spanner.getDate(spannerIndex));
@@ -523,7 +525,7 @@ public Time getTime(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case STRING:
return isNull ? null : parseTime(spanner.getString(spannerIndex));
@@ -551,7 +553,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case DATE:
return isNull ? null : JdbcTypeConverter.toSqlTimestamp(spanner.getDate(spannerIndex));
@@ -716,6 +718,7 @@ public Object getObject(int columnIndex) throws SQLException {
}
private Object getObject(Type type, int columnIndex) throws SQLException {
+ // TODO: Refactor to check based on type code.
if (type == Type.bool()) return getBoolean(columnIndex);
if (type == Type.bytes()) return getBytes(columnIndex);
if (type == Type.date()) return getDate(columnIndex);
@@ -723,7 +726,9 @@ private Object getObject(Type type, int columnIndex) throws SQLException {
return getFloat(columnIndex);
}
if (type == Type.float64()) return getDouble(columnIndex);
- if (type == Type.int64()) return getLong(columnIndex);
+ if (type == Type.int64() || type == Type.pgOid()) {
+ return getLong(columnIndex);
+ }
if (type == Type.numeric()) return getBigDecimal(columnIndex);
if (type == Type.pgNumeric()) {
final String value = getString(columnIndex);
@@ -734,7 +739,9 @@ private Object getObject(Type type, int columnIndex) throws SQLException {
}
}
if (type == Type.string()) return getString(columnIndex);
- if (type == Type.json()) return getString(columnIndex);
+ if (type == Type.json() || type == Type.pgJsonb()) {
+ return getString(columnIndex);
+ }
if (type == Type.timestamp()) return getTimestamp(columnIndex);
if (type.getCode() == Code.ARRAY) return getArray(columnIndex);
throw JdbcSqlExceptionFactory.of("Unknown type: " + type, com.google.rpc.Code.INVALID_ARGUMENT);
@@ -792,7 +799,7 @@ public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLExcepti
private BigDecimal getBigDecimal(int columnIndex, boolean fixedScale, int scale)
throws SQLException {
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
boolean isNull = isNull(columnIndex);
BigDecimal res;
switch (type) {
@@ -888,7 +895,7 @@ public Array getArray(int columnIndex) throws SQLException {
throw JdbcSqlExceptionFactory.of(
"Column with index " + columnIndex + " does not contain an array",
com.google.rpc.Code.INVALID_ARGUMENT);
- final Code elementCode = type.getArrayElementType().getCode();
+ final Code elementCode = getMainTypeCode(type.getArrayElementType());
final JdbcDataType dataType = JdbcDataType.getType(elementCode);
try {
List> elements = dataType.getArrayElements(spanner, columnIndex - 1);
@@ -907,7 +914,7 @@ public Date getDate(int columnIndex, Calendar cal) throws SQLException {
return null;
}
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case DATE:
return JdbcTypeConverter.toSqlDate(spanner.getDate(spannerIndex), cal);
@@ -941,7 +948,7 @@ public Time getTime(int columnIndex, Calendar cal) throws SQLException {
checkClosedAndValidRow();
boolean isNull = isNull(columnIndex);
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case STRING:
return isNull ? null : parseTime(spanner.getString(spannerIndex), cal);
@@ -975,7 +982,7 @@ public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException
return null;
}
int spannerIndex = columnIndex - 1;
- Code type = spanner.getColumnType(spannerIndex).getCode();
+ Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
switch (type) {
case DATE:
return JdbcTypeConverter.toSqlTimestamp(spanner.getDate(spannerIndex), cal);
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcTypeConverter.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcTypeConverter.java
index c6be0a824..1733302c5 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcTypeConverter.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcTypeConverter.java
@@ -22,6 +22,7 @@
import com.google.cloud.spanner.Type;
import com.google.cloud.spanner.Type.Code;
import com.google.cloud.spanner.Value;
+import com.google.common.base.Preconditions;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
@@ -46,6 +47,17 @@ class JdbcTypeConverter {
private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
private static final Charset UTF8 = StandardCharsets.UTF_8;
+ // TODO: Remove when this is supported in the Java client library.
+ static Code getMainTypeCode(Type type) {
+ Preconditions.checkNotNull(type);
+ switch (type.getCode()) {
+ case PG_OID:
+ return Code.INT64;
+ default:
+ return type.getCode();
+ }
+ }
+
/**
* Converts the given value from the Google {@link Type} to the Java {@link Class} type. The input
* value and the {@link Type} must be consistent with each other.
@@ -205,6 +217,8 @@ private static Value convertToSpannerValue(Object value, Type type) throws SQLEx
Arrays.asList((Double[]) ((java.sql.Array) value).getArray()));
case INT64:
return Value.int64Array(Arrays.asList((Long[]) ((java.sql.Array) value).getArray()));
+ case PG_OID:
+ return Value.pgOidArray(Arrays.asList((Long[]) ((java.sql.Array) value).getArray()));
case NUMERIC:
return Value.numericArray(
Arrays.asList((BigDecimal[]) ((java.sql.Array) value).getArray()));
@@ -238,6 +252,8 @@ private static Value convertToSpannerValue(Object value, Type type) throws SQLEx
return Value.float64((Double) value);
case INT64:
return Value.int64((Long) value);
+ case PG_OID:
+ return Value.pgOid((Long) value);
case NUMERIC:
return Value.numeric((BigDecimal) value);
case PG_NUMERIC:
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/AllTypesMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/AllTypesMockServerTest.java
index 4a1eb6a6f..cf4b7dfb5 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/AllTypesMockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/AllTypesMockServerTest.java
@@ -75,6 +75,9 @@ public void testSelectAllTypes() {
DATE_VALUE.getYear() - 1900, DATE_VALUE.getMonth() - 1, DATE_VALUE.getDayOfMonth()),
resultSet.getDate(++col));
assertEquals(TIMESTAMP_VALUE.toSqlTimestamp(), resultSet.getTimestamp(++col));
+ if (dialect == Dialect.POSTGRESQL) {
+ assertEquals(PG_OID_VALUE, resultSet.getLong(++col));
+ }
assertEquals(
BOOL_ARRAY_VALUE, Arrays.asList((Boolean[]) resultSet.getArray(++col).getArray()));
@@ -122,6 +125,10 @@ public void testSelectAllTypes() {
.map(timestamp -> timestamp == null ? null : timestamp.toSqlTimestamp())
.collect(Collectors.toList()),
Arrays.asList((Timestamp[]) resultSet.getArray(++col).getArray()));
+ if (dialect == Dialect.POSTGRESQL) {
+ assertEquals(
+ PG_OID_ARRAY_VALUE, Arrays.asList((Long[]) resultSet.getArray(++col).getArray()));
+ }
assertFalse(resultSet.next());
}
@@ -140,7 +147,7 @@ public void testInsertAllTypes() {
insertStatement
.toBuilder()
.replace(insertStatement.getSql().replaceAll("@p", "\\$"))
- .bind("p15")
+ .bind("p16")
.to(
com.google.cloud.spanner.Value.pgNumericArray(
NUMERIC_ARRAY_VALUE.stream()
@@ -179,6 +186,9 @@ public void testInsertAllTypes() {
DATE_VALUE.getMonth() - 1,
DATE_VALUE.getDayOfMonth()));
statement.setTimestamp(++param, TIMESTAMP_VALUE.toSqlTimestamp());
+ if (dialect == Dialect.POSTGRESQL) {
+ statement.setLong(++param, PG_OID_VALUE);
+ }
// TODO: Support PostgreSQL type names for creating arrays.
statement.setArray(
@@ -241,6 +251,10 @@ public void testInsertAllTypes() {
TIMESTAMP_ARRAY_VALUE.stream()
.map(timestamp -> timestamp == null ? null : timestamp.toSqlTimestamp())
.toArray(Timestamp[]::new)));
+ if (dialect == Dialect.POSTGRESQL) {
+ statement.setArray(
+ ++param, connection.createArrayOf("INT64", PG_OID_ARRAY_VALUE.toArray(new Long[0])));
+ }
assertEquals(1, statement.executeUpdate());
}
@@ -249,8 +263,8 @@ public void testInsertAllTypes() {
ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
Map paramTypes = request.getParamTypesMap();
Map params = request.getParams().getFieldsMap();
- assertEquals(20, paramTypes.size());
- assertEquals(20, params.size());
+ assertEquals(dialect == Dialect.POSTGRESQL ? 22 : 20, paramTypes.size());
+ assertEquals(dialect == Dialect.POSTGRESQL ? 22 : 20, params.size());
// Verify param types.
ImmutableList expectedTypes =
@@ -265,6 +279,10 @@ public void testInsertAllTypes() {
TypeCode.BYTES,
TypeCode.DATE,
TypeCode.TIMESTAMP);
+ if (dialect == Dialect.POSTGRESQL) {
+ expectedTypes =
+ ImmutableList.builder().addAll(expectedTypes).add(TypeCode.INT64).build();
+ }
for (int col = 0; col < expectedTypes.size(); col++) {
assertEquals(expectedTypes.get(col), paramTypes.get("p" + (col + 1)).getCode());
int arrayCol = col + expectedTypes.size();
@@ -290,6 +308,9 @@ public void testInsertAllTypes() {
params.get("p" + ++col).getStringValue());
assertEquals(DATE_VALUE.toString(), params.get("p" + ++col).getStringValue());
assertEquals(TIMESTAMP_VALUE.toString(), params.get("p" + ++col).getStringValue());
+ if (dialect == Dialect.POSTGRESQL) {
+ assertEquals(String.valueOf(PG_OID_VALUE), params.get("p" + ++col).getStringValue());
+ }
assertEquals(
BOOL_ARRAY_VALUE,
@@ -365,6 +386,13 @@ public void testInsertAllTypes() {
? null
: com.google.cloud.Timestamp.parseTimestamp(value.getStringValue()))
.collect(Collectors.toList()));
+ if (dialect == Dialect.POSTGRESQL) {
+ assertEquals(
+ PG_OID_ARRAY_VALUE,
+ params.get("p" + ++col).getListValue().getValuesList().stream()
+ .map(value -> value.hasNullValue() ? null : Long.valueOf(value.getStringValue()))
+ .collect(Collectors.toList()));
+ }
} catch (SQLException sqlException) {
throw new RuntimeException(sqlException);
}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/PartitionedQueryMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/PartitionedQueryMockServerTest.java
index 31842409b..5b521b621 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/PartitionedQueryMockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/PartitionedQueryMockServerTest.java
@@ -73,7 +73,8 @@ public void clearRequests() {
private int getExpectedColumnCount(Dialect dialect) {
// GoogleSQL also adds 4 PROTO columns.
- return dialect == Dialect.GOOGLE_STANDARD_SQL ? 24 : 20;
+ // PostgreSQL adds 2 OID columns.
+ return dialect == Dialect.GOOGLE_STANDARD_SQL ? 24 : 22;
}
private String createUrl() {
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/RandomResultSetTest.java b/src/test/java/com/google/cloud/spanner/jdbc/RandomResultSetTest.java
index 4d24068dc..e00907ee9 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/RandomResultSetTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/RandomResultSetTest.java
@@ -78,6 +78,9 @@ public void testSelectRandomResults() throws SQLException {
assertEquals(Types.BINARY, metadata.getColumnType(++col));
assertEquals(Types.DATE, metadata.getColumnType(++col));
assertEquals(Types.TIMESTAMP, metadata.getColumnType(++col));
+ if (dialect == Dialect.POSTGRESQL) {
+ assertEquals(Types.BIGINT, metadata.getColumnType(++col));
+ }
assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // boolean
assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // bigint
@@ -89,6 +92,9 @@ public void testSelectRandomResults() throws SQLException {
assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // binary
assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // date
assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // timestamp
+ if (dialect == Dialect.POSTGRESQL) {
+ assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // oid
+ }
// GoogleSQL also includes proto columns.
if (dialect == Dialect.GOOGLE_STANDARD_SQL) {
@@ -108,9 +114,7 @@ public void testSelectRandomResults() throws SQLException {
for (col = 1; col <= resultSet.getMetaData().getColumnCount(); col++) {
if (dialect == Dialect.GOOGLE_STANDARD_SQL && col > 20) {
// Proto columns are not yet supported, so skipping.
- } else if (dialect == Dialect.POSTGRESQL && col == 7) {
- // PG_JSONB is not yet recognized by the JDBC driver, so skipping.
- } else if (dialect == Dialect.POSTGRESQL && col == 15) {
+ } else if (dialect == Dialect.POSTGRESQL && col == 16) {
// getObject for ARRAY tries to get the array as a List.
// That fails if the array contains a NaN, so skipping.
} else {
@@ -130,6 +134,9 @@ public void testSelectRandomResults() throws SQLException {
resultSet.getBytes(++col);
resultSet.getDate(++col);
resultSet.getTimestamp(++col);
+ if (dialect == Dialect.POSTGRESQL) {
+ resultSet.getLong(++col); // oid
+ }
resultSet.getArray(++col);
resultSet.getArray(++col);
@@ -147,6 +154,9 @@ public void testSelectRandomResults() throws SQLException {
resultSet.getArray(++col);
resultSet.getArray(++col);
resultSet.getArray(++col);
+ if (dialect == Dialect.POSTGRESQL) {
+ resultSet.getArray(++col); // oid[]
+ }
// GoogleSQL also includes proto columns.
if (dialect == Dialect.GOOGLE_STANDARD_SQL) {
From 22f766f098944c23084776c70dbd9dba21efa59c Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Thu, 18 Apr 2024 12:57:46 +0200
Subject: [PATCH 24/29] deps: update dependency
io.opentelemetry:opentelemetry-bom to v1.37.0 (#1562)
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index f4a9fc855..4d9824792 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,7 +69,7 @@
io.opentelemetry
opentelemetry-bom
- 1.36.0
+ 1.37.0
pom
import
From 3d437076f2c699e261daf7dcb470085765dba14f Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Fri, 19 Apr 2024 08:05:28 +0200
Subject: [PATCH 25/29] deps: update dependency
com.google.cloud:sdk-platform-java-config to v3.29.0 (#1572)
---
.github/workflows/unmanaged_dependency_check.yaml | 2 +-
.kokoro/presubmit/graalvm-native-17.cfg | 2 +-
.kokoro/presubmit/graalvm-native.cfg | 2 +-
pom.xml | 2 +-
samples/snippets/pom.xml | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml
index c724f3dab..a17720ffb 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.28.1
+ uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.29.0
with:
bom-path: pom.xml
diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-17.cfg
index c2a88196e..326361c6b 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.28.1"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.29.0"
}
env_vars: {
diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native.cfg
index 94e00cbaa..1b1d4c4bf 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.28.1"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.29.0"
}
env_vars: {
diff --git a/pom.xml b/pom.xml
index 4d9824792..5657f0250 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
com.google.cloud
sdk-platform-java-config
- 3.28.1
+ 3.29.0
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 5928ec6ab..d1768c622 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -9,7 +9,7 @@
com.google.cloud
sdk-platform-java-config
- 3.28.1
+ 3.29.0
From 7c38b9d6681178a5cda42fcc6d6cd83006555058 Mon Sep 17 00:00:00 2001
From: Alice <65933803+alicejli@users.noreply.github.com>
Date: Fri, 19 Apr 2024 02:06:33 -0400
Subject: [PATCH 26/29] chore: include recommended_package in
repo-metadata.json (#1571)
---
.repo-metadata.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.repo-metadata.json b/.repo-metadata.json
index 6a760b5e4..8887ec91f 100644
--- a/.repo-metadata.json
+++ b/.repo-metadata.json
@@ -10,6 +10,7 @@
"repo_short": "java-spanner-jdbc",
"distribution_name": "com.google.cloud:google-cloud-spanner-jdbc",
"library_type": "OTHER",
- "codeowner_team": "@googleapis/api-spanner-java"
+ "codeowner_team": "@googleapis/api-spanner-java",
+ "recommended_package": "com.google.cloud.spanner.jdbc"
}
From f54d4dd1211508785cb899e0a3c9b585c0908421 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Fri, 19 Apr 2024 08:07:47 +0200
Subject: [PATCH 27/29] deps: update dependency
org.springframework.boot:spring-boot-starter-parent to v3.2.5 (#1570)
---
samples/spring-data-mybatis/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/spring-data-mybatis/pom.xml b/samples/spring-data-mybatis/pom.xml
index ef9d1d89e..92e914214 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.2.4
+ 3.2.5
From 784ac1e68ac29628fe55d7b9e772326f10ffeaec Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Fri, 19 Apr 2024 08:08:26 +0200
Subject: [PATCH 28/29] deps: update dependency
org.springframework.boot:spring-boot-starter-data-jdbc to v3.2.5 (#1569)
---
samples/spring-data-jdbc/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/spring-data-jdbc/pom.xml b/samples/spring-data-jdbc/pom.xml
index c1dc7cc00..960fe8da4 100644
--- a/samples/spring-data-jdbc/pom.xml
+++ b/samples/spring-data-jdbc/pom.xml
@@ -41,7 +41,7 @@
org.springframework.boot
spring-boot-starter-data-jdbc
- 3.2.4
+ 3.2.5
From d06be0e1d330385f0cb1e598f5f4a7f3cff65f88 Mon Sep 17 00:00:00 2001
From: "release-please[bot]"
<55107282+release-please[bot]@users.noreply.github.com>
Date: Sat, 20 Apr 2024 19:43:30 +0200
Subject: [PATCH 29/29] chore(main): release 2.16.2 (#1554)
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
---
CHANGELOG.md | 31 +++++++++++++++++++++++++++++++
pom.xml | 2 +-
samples/snapshot/pom.xml | 2 +-
versions.txt | 2 +-
4 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd7acea51..38941b4d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,36 @@
# Changelog
+## [2.16.2](https://github.com/googleapis/java-spanner-jdbc/compare/v2.16.1...v2.16.2) (2024-04-19)
+
+
+### Bug Fixes
+
+* Release ResultSet on Statement#close() ([#1567](https://github.com/googleapis/java-spanner-jdbc/issues/1567)) ([2258ae3](https://github.com/googleapis/java-spanner-jdbc/commit/2258ae3331a7e89036a202f243b9284108301fc0))
+
+
+### Dependencies
+
+* Update actions/checkout action to v4 ([#1547](https://github.com/googleapis/java-spanner-jdbc/issues/1547)) ([736e3af](https://github.com/googleapis/java-spanner-jdbc/commit/736e3afa54149dd11803bd715569afd9ec8e87f2))
+* Update actions/checkout action to v4 ([#1561](https://github.com/googleapis/java-spanner-jdbc/issues/1561)) ([6053d79](https://github.com/googleapis/java-spanner-jdbc/commit/6053d79816546130eca7a7016dc9299c079e411f))
+* Update actions/checkout digest to b4ffde6 ([#1546](https://github.com/googleapis/java-spanner-jdbc/issues/1546)) ([18c5ad4](https://github.com/googleapis/java-spanner-jdbc/commit/18c5ad4d4124f095547d50c0d2e154bc06380642))
+* Update actions/github-script action to v7 ([#1548](https://github.com/googleapis/java-spanner-jdbc/issues/1548)) ([d1d422c](https://github.com/googleapis/java-spanner-jdbc/commit/d1d422cdf0a74231c468262662fdf5ce4d27b8ef))
+* Update actions/setup-java action to v4 ([#1549](https://github.com/googleapis/java-spanner-jdbc/issues/1549)) ([cb2b911](https://github.com/googleapis/java-spanner-jdbc/commit/cb2b911b0b332e97f85974ec880a5ab7a12a7578))
+* Update actions/setup-java action to v4 ([#1563](https://github.com/googleapis/java-spanner-jdbc/issues/1563)) ([01d4de1](https://github.com/googleapis/java-spanner-jdbc/commit/01d4de1df21144e9c3bcf0b4e5192b12cd19dc82))
+* Update dependency com.google.cloud:google-cloud-spanner-bom to v6.63.0 ([#1552](https://github.com/googleapis/java-spanner-jdbc/issues/1552)) ([ac75b9f](https://github.com/googleapis/java-spanner-jdbc/commit/ac75b9faf0eaeb499428ecefda1f3285b3d28e67))
+* Update dependency com.google.cloud:google-cloud-spanner-bom to v6.64.0 ([#1565](https://github.com/googleapis/java-spanner-jdbc/issues/1565)) ([b57662f](https://github.com/googleapis/java-spanner-jdbc/commit/b57662fb65b74b329103ef63265192d7026b2c2d))
+* Update dependency com.google.cloud:sdk-platform-java-config to v3.28.1 ([#1560](https://github.com/googleapis/java-spanner-jdbc/issues/1560)) ([afcbe5e](https://github.com/googleapis/java-spanner-jdbc/commit/afcbe5ea5701a799729543c9564759570f05feb8))
+* Update dependency com.google.cloud:sdk-platform-java-config to v3.29.0 ([#1572](https://github.com/googleapis/java-spanner-jdbc/issues/1572)) ([3d43707](https://github.com/googleapis/java-spanner-jdbc/commit/3d437076f2c699e261daf7dcb470085765dba14f))
+* Update dependency io.opentelemetry:opentelemetry-bom to v1.37.0 ([#1562](https://github.com/googleapis/java-spanner-jdbc/issues/1562)) ([22f766f](https://github.com/googleapis/java-spanner-jdbc/commit/22f766f098944c23084776c70dbd9dba21efa59c))
+* Update dependency org.springframework.boot:spring-boot-starter-data-jdbc to v3.2.5 ([#1569](https://github.com/googleapis/java-spanner-jdbc/issues/1569)) ([784ac1e](https://github.com/googleapis/java-spanner-jdbc/commit/784ac1e68ac29628fe55d7b9e772326f10ffeaec))
+* Update dependency org.springframework.boot:spring-boot-starter-parent to v3.2.5 ([#1570](https://github.com/googleapis/java-spanner-jdbc/issues/1570)) ([f54d4dd](https://github.com/googleapis/java-spanner-jdbc/commit/f54d4dd1211508785cb899e0a3c9b585c0908421))
+* Update dependency org.springframework.data:spring-data-bom to v2023.1.5 ([#1564](https://github.com/googleapis/java-spanner-jdbc/issues/1564)) ([dbbcca3](https://github.com/googleapis/java-spanner-jdbc/commit/dbbcca342a83476b1f942aab23f21469cf6c8304))
+* Update stcarolas/setup-maven action to v5 ([#1550](https://github.com/googleapis/java-spanner-jdbc/issues/1550)) ([121d08e](https://github.com/googleapis/java-spanner-jdbc/commit/121d08e16db0bbb1f6041a201d620829e7121f4d))
+
+
+### Documentation
+
+* Create samples for quickstart guide ([#1536](https://github.com/googleapis/java-spanner-jdbc/issues/1536)) ([194c820](https://github.com/googleapis/java-spanner-jdbc/commit/194c8205dee9cc4144b18e219df43027b9f15cf2))
+
## [2.16.1](https://github.com/googleapis/java-spanner-jdbc/compare/v2.16.0...v2.16.1) (2024-03-22)
diff --git a/pom.xml b/pom.xml
index 5657f0250..6edcf1c68 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.16.2-SNAPSHOT
+ 2.16.2
jar
Google Cloud Spanner JDBC
https://github.com/googleapis/java-spanner-jdbc
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index baaf8bb89..f50a9b998 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -28,7 +28,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.16.2-SNAPSHOT
+ 2.16.2
diff --git a/versions.txt b/versions.txt
index 21a50e390..d27860f6a 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-spanner-jdbc:2.16.1:2.16.2-SNAPSHOT
+google-cloud-spanner-jdbc:2.16.2:2.16.2