From 9adee54bc5494d12feb9d352c284932a15b3d9ce Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 21 Apr 2022 16:04:14 +0000 Subject: [PATCH 01/24] chore(main): release 6.23.4-SNAPSHOT (#1842) :robot: I have created a release *beep* *boop* --- ### Updating meta-information for bleeding-edge SNAPSHOT release. --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- google-cloud-spanner-bom/pom.xml | 18 +++++++++--------- google-cloud-spanner/pom.xml | 4 ++-- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- grpc-google-cloud-spanner-v1/pom.xml | 4 ++-- pom.xml | 16 ++++++++-------- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- proto-google-cloud-spanner-v1/pom.xml | 4 ++-- samples/snapshot/pom.xml | 2 +- versions.txt | 14 +++++++------- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index aa6096e9e57..6978b7d8bbc 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner-bom - 6.23.3 + 6.23.4-SNAPSHOT pom com.google.cloud @@ -53,43 +53,43 @@ com.google.cloud google-cloud-spanner - 6.23.3 + 6.23.4-SNAPSHOT com.google.cloud google-cloud-spanner test-jar - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.23.3 + 6.23.4-SNAPSHOT diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 6a002f2c23a..1096af4722b 100644 --- a/google-cloud-spanner/pom.xml +++ b/google-cloud-spanner/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner - 6.23.3 + 6.23.4-SNAPSHOT jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 6.23.3 + 6.23.4-SNAPSHOT google-cloud-spanner diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index 72236c470d3..b4ebdd640f9 100644 --- a/grpc-google-cloud-spanner-admin-database-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.23.3 + 6.23.4-SNAPSHOT grpc-google-cloud-spanner-admin-database-v1 GRPC library for grpc-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.23.3 + 6.23.4-SNAPSHOT diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index f8cb84d192c..d3601e5528c 100644 --- a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.23.3 + 6.23.4-SNAPSHOT grpc-google-cloud-spanner-admin-instance-v1 GRPC library for grpc-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.23.3 + 6.23.4-SNAPSHOT diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 05dc7686d91..5620bdeb730 100644 --- a/grpc-google-cloud-spanner-v1/pom.xml +++ b/grpc-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.23.3 + 6.23.4-SNAPSHOT grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.23.3 + 6.23.4-SNAPSHOT diff --git a/pom.xml b/pom.xml index 7706a54dc78..b28d390c2dc 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 6.23.3 + 6.23.4-SNAPSHOT Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -62,37 +62,37 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.23.3 + 6.23.4-SNAPSHOT com.google.cloud google-cloud-spanner - 6.23.3 + 6.23.4-SNAPSHOT diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index 138cad9632a..966df0b52bb 100644 --- a/proto-google-cloud-spanner-admin-database-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.23.3 + 6.23.4-SNAPSHOT proto-google-cloud-spanner-admin-database-v1 PROTO library for proto-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.23.3 + 6.23.4-SNAPSHOT diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index ebca30bbba0..621d2b1528f 100644 --- a/proto-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.23.3 + 6.23.4-SNAPSHOT proto-google-cloud-spanner-admin-instance-v1 PROTO library for proto-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.23.3 + 6.23.4-SNAPSHOT diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index 17fd62d30fb..5357b897bd6 100644 --- a/proto-google-cloud-spanner-v1/pom.xml +++ b/proto-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.23.3 + 6.23.4-SNAPSHOT proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.23.3 + 6.23.4-SNAPSHOT diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index beafda0b4e7..9a9c7e44b7b 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -31,7 +31,7 @@ com.google.cloud google-cloud-spanner - 6.23.3 + 6.23.4-SNAPSHOT diff --git a/versions.txt b/versions.txt index 6bf8c6f7c30..c2576a0a8d8 100644 --- a/versions.txt +++ b/versions.txt @@ -1,10 +1,10 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:6.23.3:6.23.3 -proto-google-cloud-spanner-v1:6.23.3:6.23.3 -proto-google-cloud-spanner-admin-database-v1:6.23.3:6.23.3 -grpc-google-cloud-spanner-v1:6.23.3:6.23.3 -grpc-google-cloud-spanner-admin-instance-v1:6.23.3:6.23.3 -grpc-google-cloud-spanner-admin-database-v1:6.23.3:6.23.3 -google-cloud-spanner:6.23.3:6.23.3 +proto-google-cloud-spanner-admin-instance-v1:6.23.3:6.23.4-SNAPSHOT +proto-google-cloud-spanner-v1:6.23.3:6.23.4-SNAPSHOT +proto-google-cloud-spanner-admin-database-v1:6.23.3:6.23.4-SNAPSHOT +grpc-google-cloud-spanner-v1:6.23.3:6.23.4-SNAPSHOT +grpc-google-cloud-spanner-admin-instance-v1:6.23.3:6.23.4-SNAPSHOT +grpc-google-cloud-spanner-admin-database-v1:6.23.3:6.23.4-SNAPSHOT +google-cloud-spanner:6.23.3:6.23.4-SNAPSHOT From 0501da3e61ccecd5242b558f8f03230e4038f67a Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 21 Apr 2022 19:44:12 +0200 Subject: [PATCH 02/24] test(deps): update dependency org.mockito:mockito-core to v4.5.1 (#1837) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [org.mockito:mockito-core](https://togithub.com/mockito/mockito) | `4.4.0` -> `4.5.1` | [![age](https://badges.renovateapi.com/packages/maven/org.mockito:mockito-core/4.5.1/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/org.mockito:mockito-core/4.5.1/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/org.mockito:mockito-core/4.5.1/compatibility-slim/4.4.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/org.mockito:mockito-core/4.5.1/confidence-slim/4.4.0)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
mockito/mockito ### [`v4.5.1`](https://togithub.com/mockito/mockito/releases/v4.5.1) [Compare Source](https://togithub.com/mockito/mockito/compare/v4.5.0...v4.5.1) *Changelog generated by [Shipkit Changelog Gradle Plugin](https://togithub.com/shipkit/shipkit-changelog)* ##### 4.5.1 - 2022-04-21 - [2 commit(s)](https://togithub.com/mockito/mockito/compare/v4.5.0...v4.5.1) by Jeremy Landis, dependabot\[bot] - Fixes [#​2623](https://togithub.com/mockito/mockito/issues/2623): Use zulu distribution and java 11 for release GHA job [(#​2624)](https://togithub.com/mockito/mockito/pull/2624) - Missing errorprone module for 4.5.0 in central as release was done with jdk 8 [(#​2623)](https://togithub.com/mockito/mockito/issues/2623) - Bump kotlinVersion from 1.6.20 to 1.6.21 [(#​2622)](https://togithub.com/mockito/mockito/pull/2622) ### [`v4.5.0`](https://togithub.com/mockito/mockito/releases/v4.5.0) [Compare Source](https://togithub.com/mockito/mockito/compare/v4.4.0...v4.5.0) *Changelog generated by [Shipkit Changelog Gradle Plugin](https://togithub.com/shipkit/shipkit-changelog)* ##### 4.5.0 - 2022-04-19 - [15 commit(s)](https://togithub.com/mockito/mockito/compare/v4.4.0...v4.5.0) by Andrei Silviu Dragnea, Rafael Winterhalter, Rick Ossendrijver, dependabot\[bot] - Bump versions.errorprone from 2.13.0 to 2.13.1 [(#​2621)](https://togithub.com/mockito/mockito/pull/2621) - Bump versions.errorprone from 2.12.1 to 2.13.0 [(#​2619)](https://togithub.com/mockito/mockito/pull/2619) - Groovy inline [(#​2618)](https://togithub.com/mockito/mockito/pull/2618) - Bump actions/setup-java from 2 to 3 [(#​2615)](https://togithub.com/mockito/mockito/pull/2615) - Bump versions.bytebuddy from 1.12.8 to 1.12.9 [(#​2614)](https://togithub.com/mockito/mockito/pull/2614) - Support subclass mocks on Graal VM. [(#​2613)](https://togithub.com/mockito/mockito/pull/2613) - Bump com.diffplug.spotless from 6.4.1 to 6.4.2 [(#​2611)](https://togithub.com/mockito/mockito/pull/2611) - Bump kotlinx-coroutines-core from 1.6.0-native-mt to 1.6.1-native-mt [(#​2609)](https://togithub.com/mockito/mockito/pull/2609) - Bump versions.errorprone from 2.10.0 to 2.12.1 [(#​2608)](https://togithub.com/mockito/mockito/pull/2608) - Bump kotlinVersion from 1.6.10 to 1.6.20 [(#​2607)](https://togithub.com/mockito/mockito/pull/2607) - Bump com.diffplug.spotless from 6.4.0 to 6.4.1 [(#​2606)](https://togithub.com/mockito/mockito/pull/2606) - Bump com.diffplug.spotless from 6.3.0 to 6.4.0 [(#​2605)](https://togithub.com/mockito/mockito/pull/2605) - Bump org.eclipse.osgi from 3.17.100 to 3.17.200 [(#​2597)](https://togithub.com/mockito/mockito/pull/2597) - Deprecate ListUtil and Fields classes [(#​2593)](https://togithub.com/mockito/mockito/pull/2593) - mockito-errorprone seems not compatible with ErrorProne 2.11.0 [(#​2554)](https://togithub.com/mockito/mockito/issues/2554) - NullPointerException from Groovy metaclass methods when using mockito-inline (but not mockito-core) [(#​2522)](https://togithub.com/mockito/mockito/issues/2522)
--- ### Configuration πŸ“… **Schedule**: 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, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- google-cloud-spanner/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 1096af4722b..96510c7f504 100644 --- a/google-cloud-spanner/pom.xml +++ b/google-cloud-spanner/pom.xml @@ -318,7 +318,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test From 2d0254ddbf98b81b8141fdfd897daf8fcb788177 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 21 Apr 2022 19:52:34 +0200 Subject: [PATCH 03/24] build(deps): update dependency com.google.cloud:google-cloud-shared-config to v1.3.3 (#1838) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [com.google.cloud:google-cloud-shared-config](https://togithub.com/googleapis/java-shared-config) | `1.3.2` -> `1.3.3` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.3.3/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.3.3/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.3.3/compatibility-slim/1.3.2)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.3.3/confidence-slim/1.3.2)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
googleapis/java-shared-config ### [`v1.3.3`](https://togithub.com/googleapis/java-shared-config/blob/HEAD/CHANGELOG.md#​133-httpsgithubcomgoogleapisjava-shared-configcomparev132v133-2022-04-19) [Compare Source](https://togithub.com/googleapis/java-shared-config/compare/v1.3.2...v1.3.3)
--- ### Configuration πŸ“… **Schedule**: 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, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- google-cloud-spanner-bom/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index 6978b7d8bbc..d39ec3f6277 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -8,7 +8,7 @@ com.google.cloud google-cloud-shared-config - 1.3.2 + 1.3.3 Google Cloud Spanner BOM diff --git a/pom.xml b/pom.xml index b28d390c2dc..ed2c8d9c9e0 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ com.google.cloud google-cloud-shared-config - 1.3.2 + 1.3.3 From 088fb50a673a99e6921503be0f84b8291173240e Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 21 Apr 2022 22:04:19 +0200 Subject: [PATCH 04/24] deps: update dependency com.google.cloud:google-cloud-monitoring to v3.2.8 (#1831) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [com.google.cloud:google-cloud-monitoring](https://togithub.com/googleapis/java-monitoring) | `3.2.7` -> `3.2.8` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.2.8/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.2.8/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.2.8/compatibility-slim/3.2.7)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.2.8/confidence-slim/3.2.7)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
googleapis/java-monitoring ### [`v3.2.8`](https://togithub.com/googleapis/java-monitoring/blob/HEAD/CHANGELOG.md#​328-httpsgithubcomgoogleapisjava-monitoringcomparev327v328-2022-04-15) [Compare Source](https://togithub.com/googleapis/java-monitoring/compare/v3.2.7...v3.2.8)
--- ### Configuration πŸ“… **Schedule**: 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, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- samples/install-without-bom/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index df333e47c1b..e8b57d4d70a 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -23,7 +23,7 @@ UTF-8 0.31.0 2.1.9 - 3.2.7 + 3.2.8
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 9a9c7e44b7b..d206f32a179 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -23,7 +23,7 @@ UTF-8 0.31.0 2.1.9 - 3.2.7 + 3.2.8 From 18ce38a893d26a74f55ed165a1e33ba4f32744a2 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Fri, 22 Apr 2022 08:30:46 +0200 Subject: [PATCH 05/24] chore(deps): update dependency com.google.cloud:google-cloud-spanner to v6.23.2 (#1788) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update dependency com.google.cloud:google-cloud-spanner to v6.23.2 * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot --- README.md | 2 +- samples/install-without-bom/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f64bbab1f2b..ea3b821196f 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ If you are using Maven without BOM, add this to your dependencies: com.google.cloud google-cloud-spanner - 6.21.2 + 6.23.2 ``` diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index e8b57d4d70a..6679715f272 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -32,7 +32,7 @@ com.google.cloud google-cloud-spanner - 6.21.2 + 6.23.2 From 787ccadcba01193d541bfd1b80b055fb5d4c2bb3 Mon Sep 17 00:00:00 2001 From: Astha Mohta <35952883+asthamohta@users.noreply.github.com> Date: Fri, 22 Apr 2022 13:33:56 +0530 Subject: [PATCH 06/24] feat: Copy backup samples (#1802) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "removing samples" This reverts commit b06a3df559ee901a84a14a99624fa985b781d52d. * removing samples * changes to samples * linting * changes as per comments * Update samples/snippets/src/main/java/com/example/spanner/SpannerSample.java Co-authored-by: Knut Olav LΓΈite * Update samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java Co-authored-by: Knut Olav LΓΈite * don't merge * Revert "don't merge" This reverts commit cdf4d176ddc1acf1f7552d35a2acf3adde005b18. * linting * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * linting * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Knut Olav LΓΈite Co-authored-by: Owl Bot --- README.md | 5 +- .../com/example/spanner/CopyBackupSample.java | 101 ++++++++++++++++++ .../com/example/spanner/SpannerSample.java | 64 ++++++++--- .../com/example/spanner/SpannerSampleIT.java | 3 + 4 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java diff --git a/README.md b/README.md index ea3b821196f..86705e8fcca 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,13 @@ implementation 'com.google.cloud:google-cloud-spanner' If you are using Gradle without BOM, add this to your dependencies ```Groovy -implementation 'com.google.cloud:google-cloud-spanner:6.23.2' +implementation 'com.google.cloud:google-cloud-spanner:6.23.3' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.23.2" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.23.3" ``` ## Authentication @@ -252,6 +252,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | Async Runner Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AsyncRunnerExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/AsyncRunnerExample.java) | | Async Transaction Manager Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AsyncTransactionManagerExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/AsyncTransactionManagerExample.java) | | Batch Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/BatchSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/BatchSample.java) | +| Copy Backup Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java) | | Create Backup With Encryption Key | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/CreateBackupWithEncryptionKey.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/CreateBackupWithEncryptionKey.java) | | Create Database With Default Leader Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSample.java) | | Create Database With Encryption Key | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithEncryptionKey.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithEncryptionKey.java) | diff --git a/samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java b/samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java new file mode 100644 index 00000000000..795edcb15a6 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java @@ -0,0 +1,101 @@ +/* + * Copyright 2022 Google Inc. + * + * 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; + +// [START spanner_copy_backup] + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Backup; +import com.google.cloud.spanner.BackupId; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.SpannerOptions; +import com.google.spanner.admin.database.v1.CopyBackupMetadata; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +public class CopyBackupSample { + static void copyBackup() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String sourceBackupId = "my-backup"; + String destinationBackupId = "my-destination-backup"; + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + copyBackup(databaseAdminClient, projectId, instanceId, sourceBackupId, destinationBackupId); + } + } + + static void copyBackup( + DatabaseAdminClient databaseAdminClient, + String projectId, + String instanceId, + String sourceBackupId, + String destinationBackupId) { + + Timestamp expireTime = + Timestamp.ofTimeMicroseconds( + TimeUnit.MICROSECONDS.convert( + System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), + TimeUnit.MILLISECONDS)); + // Creates a copy of an existing backup. + Backup destinationBackup = + databaseAdminClient + .newBackupBuilder(BackupId.of(projectId, instanceId, destinationBackupId)) + .setExpireTime(expireTime) + .build(); + + // Initiate the request which returns an OperationFuture. + System.out.println("Copying backup [" + destinationBackup.getId() + "]..."); + OperationFuture operation = + databaseAdminClient.copyBackup( + BackupId.of(projectId, instanceId, sourceBackupId), destinationBackup); + try { + // Wait for the backup operation to complete. + destinationBackup = operation.get(); + System.out.println("Copied backup [" + destinationBackup.getId() + "]"); + } catch (ExecutionException e) { + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + throw SpannerExceptionFactory.propagateInterrupt(e); + } + // Load the metadata of the new backup from the server. + destinationBackup = destinationBackup.reload(); + System.out.println( + String.format( + "Backup %s of size %d bytes was copied at %s for version of database at %s", + destinationBackup.getId().getName(), + destinationBackup.getSize(), + LocalDateTime.ofEpochSecond( + destinationBackup.getProto().getCreateTime().getSeconds(), + destinationBackup.getProto().getCreateTime().getNanos(), + OffsetDateTime.now().getOffset()), + LocalDateTime.ofEpochSecond( + destinationBackup.getProto().getVersionTime().getSeconds(), + destinationBackup.getProto().getVersionTime().getNanos(), + OffsetDateTime.now().getOffset()))); + return; + } +} +// [END spanner_copy_backup] diff --git a/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java index d0ccd108145..0cda2a35119 100644 --- a/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java @@ -56,6 +56,7 @@ import com.google.common.io.BaseEncoding; import com.google.longrunning.Operation; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.spanner.admin.database.v1.CopyBackupMetadata; import com.google.spanner.admin.database.v1.CreateBackupMetadata; import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; import com.google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata; @@ -1659,7 +1660,8 @@ static void cancelCreateBackup( // [END spanner_cancel_backup_create] // [START spanner_list_backup_operations] - static void listBackupOperations(InstanceAdminClient instanceAdminClient, DatabaseId databaseId) { + static void listBackupOperations( + InstanceAdminClient instanceAdminClient, DatabaseId databaseId, BackupId backupId) { Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); // Get create backup operations for the sample database. Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( @@ -1667,13 +1669,14 @@ static void listBackupOperations(InstanceAdminClient instanceAdminClient, Databa TimeUnit.HOURS), 0); String filter = String.format( - "(metadata.database:%s) AND " - + "(metadata.@type:type.googleapis.com/" - + "google.spanner.admin.database.v1.CreateBackupMetadata) AND " - + "(metadata.progress.start_time > \"%s\")", - databaseId.getName(), last24Hours); - Page operations = instance.listBackupOperations(Options.filter(filter)); - for (Operation op : operations.iterateAll()) { + "(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.CreateBackupMetadata) " + + "AND (metadata.database:%s)", + databaseId.getName()); + Page createBackupOperations = instance.listBackupOperations( + Options.filter(filter)); + System.out.println("Create Backup Operations:"); + for (Operation op : createBackupOperations.iterateAll()) { try { CreateBackupMetadata metadata = op.getMetadata().unpack(CreateBackupMetadata.class); System.out.println( @@ -1687,6 +1690,30 @@ static void listBackupOperations(InstanceAdminClient instanceAdminClient, Databa System.err.println(e.getMessage()); } } + // Get copy backup operations for the sample database. + filter = + String.format( + "(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.CopyBackupMetadata) " + + "AND (metadata.source_backup:%s)", + backupId.getName()); + Page copyBackupOperations = instance.listBackupOperations(Options.filter(filter)); + System.out.println("Copy Backup Operations:"); + for (Operation op : copyBackupOperations.iterateAll()) { + try { + CopyBackupMetadata copyBackupMetadata = + op.getMetadata().unpack(CopyBackupMetadata.class); + System.out.println( + String.format( + "Copy Backup %s on backup %s pending: %d%% complete", + copyBackupMetadata.getName(), + copyBackupMetadata.getSourceBackup(), + copyBackupMetadata.getProgress().getProgressPercent())); + } catch (InvalidProtocolBufferException e) { + // The returned operation does not contain CopyBackupMetadata. + System.err.println(e.getMessage()); + } + } } // [END spanner_list_backup_operations] @@ -1840,13 +1867,19 @@ static void updateBackup(DatabaseAdminClient dbAdminClient, BackupId backupId) { TimeUnit.SECONDS.toMicros(backup.getExpireTime().getSeconds()) + TimeUnit.NANOSECONDS.toMicros(backup.getExpireTime().getNanos()) + TimeUnit.DAYS.toMicros(30L)); + // New Expire Time must be less than Max Expire Time + expireTime = expireTime.compareTo(backup.getMaxExpireTime()) + < 0 ? expireTime : backup.getMaxExpireTime(); + int timeDiff = expireTime.compareTo(backup.getExpireTime()); + Timestamp newExpireTime = (timeDiff < 0) ? expireTime : backup.getExpireTime(); + System.out.println(String.format( "Updating expire time of backup [%s] to %s...", backupId.toString(), LocalDateTime.ofEpochSecond( - expireTime.getSeconds(), - expireTime.getNanos(), - OffsetDateTime.now().getOffset()).toString())); + expireTime.getSeconds(), + expireTime.getNanos(), + OffsetDateTime.now().getOffset()).toString())); // Update expire time. backup = backup.toBuilder().setExpireTime(expireTime).build(); @@ -2048,7 +2081,7 @@ static void run( BackupId.of(backup.getInstanceId(), backup.getBackup() + "_cancel")); break; case "listbackupoperations": - listBackupOperations(instanceAdminClient, database); + listBackupOperations(instanceAdminClient, database, backup); break; case "listdatabaseoperations": listDatabaseOperations(instanceAdminClient, dbAdminClient, database.getInstanceId()); @@ -2144,14 +2177,14 @@ static void printUsageAndExit() { System.err.println(" SpannerExample querywithqueryoptions my-instance example-db"); System.err.println(" SpannerExample createbackup my-instance example-db"); System.err.println(" SpannerExample listbackups my-instance example-db"); - System.err.println(" SpannerExample listbackupoperations my-instance example-db"); + System.err.println(" SpannerExample listbackupoperations my-instance example-db backup-id"); System.err.println(" SpannerExample listdatabaseoperations my-instance example-db"); System.err.println(" SpannerExample restorebackup my-instance example-db"); System.exit(1); } public static void main(String[] args) throws Exception { - if (args.length != 3) { + if (args.length != 3 && args.length != 4) { printUsageAndExit(); } // [START init_client] @@ -2176,6 +2209,9 @@ public static void main(String[] args) throws Exception { "%s_%02d", db.getDatabase(), LocalDate.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR)); BackupId backup = BackupId.of(db.getInstanceId(), backupName); + if (args.length == 4) { + backupName = args[3]; + } // [START init_client] DatabaseClient dbClient = spanner.getDatabaseClient(db); diff --git a/samples/snippets/src/test/java/com/example/spanner/SpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/SpannerSampleIT.java index 20b8381b542..aeff6e8b778 100644 --- a/samples/snippets/src/test/java/com/example/spanner/SpannerSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/SpannerSampleIT.java @@ -17,6 +17,7 @@ package com.example.spanner; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; import com.google.cloud.Timestamp; import com.google.cloud.spanner.Backup; @@ -328,6 +329,8 @@ public void testSample() throws Exception { "Backup %s on database %s pending:", backupId.getName(), dbId.getName())); + assertTrue("Out does not contain copy backup operations", out.contains( + "Copy Backup Operations")); } catch (SpannerException e) { assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); assertThat(e.getMessage()).contains("Cannot evaluate filter expression"); From 08d1e16e5b8bd66a4ff1e0a40d9dc40973f430c8 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Fri, 22 Apr 2022 16:12:12 +0200 Subject: [PATCH 07/24] chore(deps): update dependency com.google.cloud:google-cloud-spanner to v6.23.3 (#1848) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [com.google.cloud:google-cloud-spanner](https://togithub.com/googleapis/java-spanner) | `6.23.2` -> `6.23.3` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-spanner/6.23.3/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-spanner/6.23.3/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-spanner/6.23.3/compatibility-slim/6.23.2)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-spanner/6.23.3/confidence-slim/6.23.2)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
googleapis/java-spanner ### [`v6.23.3`](https://togithub.com/googleapis/java-spanner/blob/HEAD/CHANGELOG.md#​6233-httpsgithubcomgoogleapisjava-spannercomparev6232v6233-2022-04-21) [Compare Source](https://togithub.com/googleapis/java-spanner/compare/v6.23.2...v6.23.3)
--- ### Configuration πŸ“… **Schedule**: At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. β™» **Rebasing**: Renovate will not automatically rebase this PR, because other commits have been found. πŸ”• **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- README.md | 2 +- samples/install-without-bom/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 86705e8fcca..cd91bbd4e5e 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ If you are using Maven without BOM, add this to your dependencies: com.google.cloud google-cloud-spanner - 6.23.2 + 6.23.3 ``` diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 6679715f272..9dd0f7869a2 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -32,7 +32,7 @@ com.google.cloud google-cloud-spanner - 6.23.2 + 6.23.3 From c4d17d0a33ab17b37830150c132d5afbdfff35d4 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Fri, 22 Apr 2022 16:42:38 +0200 Subject: [PATCH 08/24] build(deps): update dependency org.sonatype.plugins:nexus-staging-maven-plugin to v1.6.13 (#1843) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [org.sonatype.plugins:nexus-staging-maven-plugin](http://www.sonatype.com/) ([source](https://togithub.com/sonatype/nexus-maven-plugins)) | `1.6.11` -> `1.6.13` | [![age](https://badges.renovateapi.com/packages/maven/org.sonatype.plugins:nexus-staging-maven-plugin/1.6.13/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/org.sonatype.plugins:nexus-staging-maven-plugin/1.6.13/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/org.sonatype.plugins:nexus-staging-maven-plugin/1.6.13/compatibility-slim/1.6.11)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/org.sonatype.plugins:nexus-staging-maven-plugin/1.6.13/confidence-slim/1.6.11)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
sonatype/nexus-maven-plugins ### [`v1.6.13`](https://togithub.com/sonatype/nexus-maven-plugins/compare/release-1.6.12...release-1.6.13) [Compare Source](https://togithub.com/sonatype/nexus-maven-plugins/compare/release-1.6.12...release-1.6.13) ### [`v1.6.12`](https://togithub.com/sonatype/nexus-maven-plugins/compare/release-1.6.11...release-1.6.12) [Compare Source](https://togithub.com/sonatype/nexus-maven-plugins/compare/release-1.6.11...release-1.6.12)
--- ### Configuration πŸ“… **Schedule**: At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. β™» **Rebasing**: Renovate will not automatically rebase this PR, because other commits have been found. πŸ”• **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- samples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/pom.xml b/samples/pom.xml index 8271ea6812d..b46876310dd 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -46,7 +46,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.11 + 1.6.13 true From 4d6bb2dd233fba60d213d36f15aead67dff57dec Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Fri, 22 Apr 2022 21:34:11 +0200 Subject: [PATCH 09/24] deps: update dependency com.google.cloud:google-cloud-monitoring to v3.2.9 (#1851) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [com.google.cloud:google-cloud-monitoring](https://togithub.com/googleapis/java-monitoring) | `3.2.8` -> `3.2.9` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.2.9/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.2.9/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.2.9/compatibility-slim/3.2.8)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-monitoring/3.2.9/confidence-slim/3.2.8)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
googleapis/java-monitoring ### [`v3.2.9`](https://togithub.com/googleapis/java-monitoring/blob/HEAD/CHANGELOG.md#​329-httpsgithubcomgoogleapisjava-monitoringcomparev328v329-2022-04-22) [Compare Source](https://togithub.com/googleapis/java-monitoring/compare/v3.2.8...v3.2.9)
--- ### Configuration πŸ“… **Schedule**: 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, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- samples/install-without-bom/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 9dd0f7869a2..418e4e7164b 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -23,7 +23,7 @@ UTF-8 0.31.0 2.1.9 - 3.2.8 + 3.2.9 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index d206f32a179..fdff73811e1 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -23,7 +23,7 @@ UTF-8 0.31.0 2.1.9 - 3.2.8 + 3.2.9 From 0cff0e7a2979e6168b4442f6735c8d303bcd4b59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Mon, 25 Apr 2022 12:22:19 +0200 Subject: [PATCH 10/24] test: use an AtomicInteger for dbseq (#1853) Use an AtomicInteger for dbseq to ensure unique database names if multiple tests try to create a database in parallel. Updates https://github.com/googleapis/java-spanner-jdbc/issues/820 --- .../cloud/spanner/testing/RemoteSpannerHelper.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java index b3e9abf0740..cf9468f9244 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/testing/RemoteSpannerHelper.java @@ -48,10 +48,10 @@ public class RemoteSpannerHelper { private final SpannerOptions options; private final Spanner client; private final InstanceId instanceId; - private static int dbSeq; - private static int dbPrefix = new Random().nextInt(Integer.MAX_VALUE); + private static final AtomicInteger dbSeq = new AtomicInteger(); + private static final int dbPrefix = new Random().nextInt(Integer.MAX_VALUE); private static final AtomicInteger backupSeq = new AtomicInteger(); - private static int backupPrefix = new Random().nextInt(Integer.MAX_VALUE); + private static final int backupPrefix = new Random().nextInt(Integer.MAX_VALUE); private final List dbs = new ArrayList<>(); protected RemoteSpannerHelper(SpannerOptions options, InstanceId instanceId, Spanner client) { @@ -104,7 +104,7 @@ public Database createTestDatabase(String... statements) throws SpannerException * Returns a database id which is guaranteed to be unique within the context of this environment. */ public String getUniqueDatabaseId() { - return String.format("testdb_%d_%04d", dbPrefix, dbSeq++); + return String.format("testdb_%d_%04d", dbPrefix, dbSeq.incrementAndGet()); } /** @@ -159,7 +159,7 @@ public void cleanUp() { int numDropped = 0; for (Database db : dbs) { try { - logger.log(Level.FINE, "Dropping test database {0}", db.getId()); + logger.log(Level.INFO, "Dropping test database {0}", db.getId()); db.drop(); ++numDropped; } catch (SpannerException e) { From f865e2549c40c4a559c66f0a9aba801e48f5978e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 27 Apr 2022 11:02:01 +0200 Subject: [PATCH 11/24] chore: remove gsql parsing escape (#1850) --- .../connection/AbstractStatementParser.java | 42 +----- .../connection/PostgreSQLStatementParser.java | 1 - .../connection/SpannerStatementParser.java | 1 - .../connection/StatementParserTest.java | 122 ------------------ 4 files changed, 4 insertions(+), 162 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java index c17e4dd7395..25465990d2f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java @@ -54,12 +54,6 @@ public abstract class AbstractStatementParser { SpannerStatementParser.class, Dialect.POSTGRESQL, PostgreSQLStatementParser.class); - private static final String GSQL_STATEMENT = "/*GSQL*/"; - - /* Checks if the SQL statement starts with /*GSQL*/ - private boolean isGoogleSql(String sql) { - return sql.startsWith(GSQL_STATEMENT); - } /** Get an instance of {@link AbstractStatementParser} for the specified dialect. */ public static AbstractStatementParser getInstance(Dialect dialect) { @@ -317,11 +311,9 @@ ClientSideStatement getClientSideStatement() { static final Set ddlStatements = ImmutableSet.of("CREATE", "DROP", "ALTER"); static final Set selectStatements = ImmutableSet.of("SELECT", "WITH", "SHOW"); static final Set dmlStatements = ImmutableSet.of("INSERT", "UPDATE", "DELETE"); - private final Dialect dialect; private final Set statements; - AbstractStatementParser(Dialect dialect, Set statements) { - this.dialect = dialect; + AbstractStatementParser(Set statements) { this.statements = statements; } @@ -427,12 +419,7 @@ public boolean isUpdateStatement(String sql) { private boolean statementStartsWith(String sql, Iterable checkStatements) { Preconditions.checkNotNull(sql); - String[] tokens; - if (isGoogleSql(sql)) { - tokens = sql.substring(GSQL_STATEMENT.length()).split("\\s+", 2); - } else { - tokens = sql.split("\\s+", 2); - } + String[] tokens = sql.split("\\s+", 2); int checkIndex = 0; if (supportsExplain() && tokens[0].equalsIgnoreCase("EXPLAIN")) { checkIndex = 1; @@ -467,20 +454,7 @@ private boolean statementStartsWith(String sql, Iterable checkStatements @InternalApi public String removeCommentsAndTrim(String sql) { - switch (dialect) { - case POSTGRESQL: - if (isGoogleSql(sql.trim())) { - return GSQL_STATEMENT - + INSTANCES.get(Dialect.GOOGLE_STANDARD_SQL).removeCommentsAndTrimInternal(sql); - } else { - return removeCommentsAndTrimInternal(sql); - } - case GOOGLE_STANDARD_SQL: - return removeCommentsAndTrimInternal(sql); - default: - throw SpannerExceptionFactory.newSpannerException( - ErrorCode.INTERNAL, "Unknown dialect: " + dialect); - } + return removeCommentsAndTrimInternal(sql); } /** Removes any statement hints at the beginning of the statement. */ @@ -517,15 +491,7 @@ abstract ParametersInfo convertPositionalParametersToNamedParametersInternal( @InternalApi public ParametersInfo convertPositionalParametersToNamedParameters(char paramChar, String sql) { - if (dialect == Dialect.POSTGRESQL && isGoogleSql(sql.trim())) { - return INSTANCES - .get(Dialect.GOOGLE_STANDARD_SQL) - .convertPositionalParametersToNamedParametersInternal(paramChar, sql); - } else { - return INSTANCES - .get(dialect) - .convertPositionalParametersToNamedParametersInternal(paramChar, sql); - } + return convertPositionalParametersToNamedParametersInternal(paramChar, sql); } /** Convenience method that is used to estimate the number of parameters in a SQL statement. */ diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/PostgreSQLStatementParser.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/PostgreSQLStatementParser.java index 60ccb34cf15..38f8ed08f99 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/PostgreSQLStatementParser.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/PostgreSQLStatementParser.java @@ -31,7 +31,6 @@ public class PostgreSQLStatementParser extends AbstractStatementParser { PostgreSQLStatementParser() throws CompileException { super( - Dialect.POSTGRESQL, Collections.unmodifiableSet( ClientSideStatements.getInstance(Dialect.POSTGRESQL).getCompiledStatements())); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerStatementParser.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerStatementParser.java index 8bfdde42bff..f223cbb935a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerStatementParser.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerStatementParser.java @@ -31,7 +31,6 @@ public class SpannerStatementParser extends AbstractStatementParser { public SpannerStatementParser() throws CompileException { super( - Dialect.GOOGLE_STANDARD_SQL, Collections.unmodifiableSet( ClientSideStatements.getInstance(Dialect.GOOGLE_STANDARD_SQL).getCompiledStatements())); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java index 65b769b9d60..da0ceacd50d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java @@ -182,31 +182,6 @@ public void testGoogleStandardSQLRemoveCommentsGsql() { .isEqualTo("SELECT * FROM FOO"); } - @Test - public void testPostgreSQLDialectRemoveCommentsGsql() { - assumeTrue(dialect == Dialect.POSTGRESQL); - - assertThat(parser.removeCommentsAndTrim("/*GSQL*/")).isEqualTo("/*GSQL*/"); - assertThat(parser.removeCommentsAndTrim("/*GSQL*/SELECT * FROM FOO")) - .isEqualTo("/*GSQL*/SELECT * FROM FOO"); - assertThat( - parser.removeCommentsAndTrim( - "/*GSQL*/-- This is a one line comment\nSELECT * FROM FOO")) - .isEqualTo("/*GSQL*/SELECT * FROM FOO"); - assertThat( - parser.removeCommentsAndTrim( - "/*GSQL*//* This is a simple multi line comment */\nSELECT * FROM FOO")) - .isEqualTo("/*GSQL*/SELECT * FROM FOO"); - assertThat( - parser.removeCommentsAndTrim( - "/*GSQL*//* This is a \nmulti line comment */\nSELECT * FROM FOO")) - .isEqualTo("/*GSQL*/SELECT * FROM FOO"); - assertThat( - parser.removeCommentsAndTrim( - "/*GSQL*//* This\nis\na\nmulti\nline\ncomment */\nSELECT * FROM FOO")) - .isEqualTo("/*GSQL*/SELECT * FROM FOO"); - } - @Test public void testStatementWithCommentContainingSlash() { String sql = @@ -1003,103 +978,6 @@ private void testParseStatementWithOneParame assertParsing(withSuffix(")", statement), statementClass); } - @Test - public void testConvertPositionalParametersToNamedParametersWithGsqlException() { - assertThat( - parser.convertPositionalParametersToNamedParameters( - '?', "/*GSQL*/select * from foo where name=?") - .sqlWithNamedParameters) - .isEqualTo("/*GSQL*/select * from foo where name=@p1"); - assertThat( - parser.convertPositionalParametersToNamedParameters( - '?', "/*GSQL*/?'?test?\"?test?\"?'?") - .sqlWithNamedParameters) - .isEqualTo("/*GSQL*/@p1'?test?\"?test?\"?'@p2"); - assertThat( - parser.convertPositionalParametersToNamedParameters('?', "/*GSQL*/?'?it\\'?s'?") - .sqlWithNamedParameters) - .isEqualTo("/*GSQL*/@p1'?it\\'?s'@p2"); - assertThat( - parser.convertPositionalParametersToNamedParameters('?', "/*GSQL*/?'?it\\\"?s'?") - .sqlWithNamedParameters) - .isEqualTo("/*GSQL*/@p1'?it\\\"?s'@p2"); - assertThat( - parser.convertPositionalParametersToNamedParameters('?', "/*GSQL*/?\"?it\\\"?s\"?") - .sqlWithNamedParameters) - .isEqualTo("/*GSQL*/@p1\"?it\\\"?s\"@p2"); - assertThat( - parser.convertPositionalParametersToNamedParameters('?', "/*GSQL*/?'''?it\\'?s'''?") - .sqlWithNamedParameters) - .isEqualTo("/*GSQL*/@p1'''?it\\'?s'''@p2"); - assertThat( - parser.convertPositionalParametersToNamedParameters( - '?', "/*GSQL*/?\"\"\"?it\\\"?s\"\"\"?") - .sqlWithNamedParameters) - .isEqualTo("/*GSQL*/@p1\"\"\"?it\\\"?s\"\"\"@p2"); - - assertThat( - parser.convertPositionalParametersToNamedParameters( - '?', - "/*GSQL*/select 1, ?, 'test?test', \"test?test\", foo.* from `foo` where col1=? and col2='test' and col3=? and col4='?' and col5=\"?\" and col6='?''?''?'") - .sqlWithNamedParameters, - is( - equalTo( - "/*GSQL*/select 1, @p1, 'test?test', \"test?test\", foo.* from `foo` where col1=@p2 and col2='test' and col3=@p3 and col4='?' and col5=\"?\" and col6='?''?''?'"))); - - assertThat( - parser.convertPositionalParametersToNamedParameters( - '?', - "/*GSQL*/select * " - + "from foo " - + "where name=? " - + "and col2 like ? " - + "and col3 > ?") - .sqlWithNamedParameters, - is( - equalTo( - "/*GSQL*/select * " - + "from foo " - + "where name=@p1 " - + "and col2 like @p2 " - + "and col3 > @p3"))); - assertThat( - parser.convertPositionalParametersToNamedParameters( - '?', "/*GSQL*/select * " + "from foo " + "where id between ? and ?") - .sqlWithNamedParameters, - is(equalTo("/*GSQL*/select * " + "from foo " + "where id between @p1 and @p2"))); - assertThat( - parser.convertPositionalParametersToNamedParameters( - '?', "/*GSQL*/select * " + "from foo " + "limit ? offset ?") - .sqlWithNamedParameters, - is(equalTo("/*GSQL*/select * " + "from foo " + "limit @p1 offset @p2"))); - assertThat( - parser.convertPositionalParametersToNamedParameters( - '?', - "/*GSQL*/select * " - + "from foo " - + "where col1=? " - + "and col2 like ? " - + "and col3 > ? " - + "and col4 < ? " - + "and col5 != ? " - + "and col6 not in (?, ?, ?) " - + "and col7 in (?, ?, ?) " - + "and col8 between ? and ?") - .sqlWithNamedParameters, - is( - equalTo( - "/*GSQL*/select * " - + "from foo " - + "where col1=@p1 " - + "and col2 like @p2 " - + "and col3 > @p3 " - + "and col4 < @p4 " - + "and col5 != @p5 " - + "and col6 not in (@p6, @p7, @p8) " - + "and col7 in (@p9, @p10, @p11) " - + "and col8 between @p12 and @p13"))); - } - @Test public void testGoogleStandardSQLDialectConvertPositionalParametersToNamedParameters() { assumeTrue(dialect == Dialect.GOOGLE_STANDARD_SQL); From ce599939052960f8e99a1a1edef87ca713902988 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 27 Apr 2022 18:06:12 +0200 Subject: [PATCH 12/24] chore(deps): update dependency com.google.cloud:libraries-bom to v25.2.0 (#1855) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](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/GoogleCloudPlatform/cloud-opensource-java)) | `25.1.0` -> `25.2.0` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:libraries-bom/25.2.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:libraries-bom/25.2.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:libraries-bom/25.2.0/compatibility-slim/25.1.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:libraries-bom/25.2.0/confidence-slim/25.1.0)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration πŸ“… **Schedule**: At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. β™» **Rebasing**: Renovate will not automatically rebase this PR, because other commits have been found. πŸ”• **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- README.md | 4 ++-- samples/snippets/pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cd91bbd4e5e..8f78233506a 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file com.google.cloud libraries-bom - 25.1.0 + 25.2.0 pom import @@ -49,7 +49,7 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies ```Groovy -implementation platform('com.google.cloud:libraries-bom:25.1.0') +implementation platform('com.google.cloud:libraries-bom:25.2.0') implementation 'com.google.cloud:google-cloud-spanner' ``` diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 3474fa57564..b76670a913e 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -33,7 +33,7 @@ com.google.cloud libraries-bom - 25.1.0 + 25.2.0 pom import From 049635d4bc3210bd9ce41444f17c8b9d67af969a Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 27 Apr 2022 22:21:53 +0200 Subject: [PATCH 13/24] deps: update dependency com.google.cloud:google-cloud-trace to v2.1.11 (#1799) --- samples/install-without-bom/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 418e4e7164b..45dfb794144 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -22,7 +22,7 @@ 1.8 UTF-8 0.31.0 - 2.1.9 + 2.1.11 3.2.9 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index fdff73811e1..2868f893ab4 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -22,7 +22,7 @@ 1.8 UTF-8 0.31.0 - 2.1.9 + 2.1.11 3.2.9 From 40110feb22986c6b5dac6885eae7f0b331aede61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 28 Apr 2022 14:46:09 +0200 Subject: [PATCH 14/24] feat: support CREATE DATABASE in Connection API (#1845) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: support CREATE DATABASE in Connection API Adds support for the CREATE DATABASE statement in the Connection API. The statement can only be used with a SingleUseTransaction (i.e. auto commit mode). It is not supported in DDL batches. The database that is created will have the same dialect as the current database that the user is connected to. Fixes #1884 * fix: add clirr ignore * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot --- .../clirr-ignored-differences.xml | 6 ++++ .../cloud/spanner/DatabaseAdminClient.java | 33 +++++++++++++++++++ .../spanner/DatabaseAdminClientImpl.java | 20 +++++++++++ .../cloud/spanner/connection/DdlBatch.java | 3 ++ .../cloud/spanner/connection/DdlClient.java | 24 ++++++++++++++ .../connection/SingleUseTransaction.java | 15 ++++++--- .../spanner/connection/DdlBatchTest.java | 12 +++++++ .../spanner/connection/DdlClientTest.java | 21 ++++++++++++ .../connection/SingleUseTransactionTest.java | 15 +++++++++ .../spanner/connection/it/ITDdlTest.java | 23 +++++++++++++ 10 files changed, 167 insertions(+), 5 deletions(-) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 899b44e3546..450eacde322 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -60,4 +60,10 @@ com/google/cloud/spanner/spi/v1/SpannerRpc com.google.api.gax.longrunning.OperationFuture copyBackup(com.google.cloud.spanner.BackupId, com.google.cloud.spanner.Backup) + + 7012 + com/google/cloud/spanner/DatabaseAdminClient + com.google.api.gax.longrunning.OperationFuture createDatabase(java.lang.String, java.lang.String, com.google.cloud.spanner.Dialect, java.lang.Iterable) + + diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClient.java index d3aa8b4d18f..26c48507c45 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClient.java @@ -70,6 +70,39 @@ public interface DatabaseAdminClient { OperationFuture createDatabase( String instanceId, String databaseId, Iterable statements) throws SpannerException; + /** + * Creates a new database in a Cloud Spanner instance with the given {@link Dialect}. + * + *

Example to create database. + * + *

{@code
+   * String instanceId = "my_instance_id";
+   * String createDatabaseStatement = "CREATE DATABASE \"my-database\"";
+   * Operation op = dbAdminClient
+   *     .createDatabase(
+   *         instanceId,
+   *         createDatabaseStatement,
+   *         Dialect.POSTGRESQL
+   *         Collections.emptyList());
+   * Database db = op.waitFor().getResult();
+   * }
+ * + * @param instanceId the id of the instance in which to create the database. + * @param createDatabaseStatement the CREATE DATABASE statement for the database. This statement + * must use the dialect for the new database. + * @param dialect the dialect that the new database should use. + * @param statements DDL statements to run while creating the database, for example {@code CREATE + * TABLE MyTable ( ... )}. This should not include {@code CREATE DATABASE} statement. + */ + default OperationFuture createDatabase( + String instanceId, + String createDatabaseStatement, + Dialect dialect, + Iterable statements) + throws SpannerException { + throw new UnsupportedOperationException("Unimplemented"); + } + /** * Creates a database in a Cloud Spanner instance. Any configuration options in the {@link * Database} instance will be included in the {@link CreateDatabaseRequest}. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClientImpl.java index 3285dde5f5e..86b0bdbb8de 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseAdminClientImpl.java @@ -332,6 +332,26 @@ public OperationFuture createDatabase( final Dialect dialect = Preconditions.checkNotNull(database.getDialect()); final String createStatement = dialect.createDatabaseStatementFor(database.getId().getDatabase()); + + return createDatabase(createStatement, database, statements); + } + + @Override + public OperationFuture createDatabase( + String instanceId, + String createDatabaseStatement, + Dialect dialect, + Iterable statements) + throws SpannerException { + Database database = + newDatabaseBuilder(DatabaseId.of(projectId, instanceId, "")).setDialect(dialect).build(); + + return createDatabase(createDatabaseStatement, database, statements); + } + + private OperationFuture createDatabase( + String createStatement, Database database, Iterable statements) + throws SpannerException { OperationFuture rawOperationFuture = rpc.createDatabase( diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java index 6d34c76fde8..0a34e6b5767 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java @@ -188,6 +188,9 @@ public ApiFuture executeDdlAsync(ParsedStatement ddl) { "Only DDL statements are allowed. \"" + ddl.getSqlWithoutComments() + "\" is not a DDL-statement."); + Preconditions.checkArgument( + !DdlClient.isCreateDatabaseStatement(ddl.getSqlWithoutComments()), + "CREATE DATABASE is not supported in DDL batches."); statements.add(ddl.getSqlWithoutComments()); return ApiFutures.immediateFuture(null); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlClient.java index f3c9cdba037..fedf60d7a91 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlClient.java @@ -17,9 +17,14 @@ package com.google.cloud.spanner.connection; import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.ErrorCode; +import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import java.util.Collections; import java.util.List; @@ -79,6 +84,13 @@ private DdlClient(Builder builder) { this.databaseName = builder.databaseName; } + OperationFuture executeCreateDatabase( + String createStatement, Dialect dialect) { + Preconditions.checkArgument(isCreateDatabaseStatement(createStatement)); + return dbAdminClient.createDatabase( + instanceId, createStatement, dialect, Collections.emptyList()); + } + /** Execute a single DDL statement. */ OperationFuture executeDdl(String ddl) { return executeDdl(Collections.singletonList(ddl)); @@ -86,6 +98,18 @@ OperationFuture executeDdl(String ddl) { /** Execute a list of DDL statements as one operation. */ OperationFuture executeDdl(List statements) { + if (statements.stream().anyMatch(DdlClient::isCreateDatabaseStatement)) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, "CREATE DATABASE is not supported in a DDL batch"); + } return dbAdminClient.updateDatabaseDdl(instanceId, databaseName, statements, null); } + + /** Returns true if the statement is a `CREATE DATABASE ...` statement. */ + static boolean isCreateDatabaseStatement(String statement) { + String[] tokens = statement.split("\\s+", 3); + return tokens.length >= 2 + && tokens[0].equalsIgnoreCase("CREATE") + && tokens[1].equalsIgnoreCase("DATABASE"); + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java index f34e6f72e71..2747b07778b 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SingleUseTransaction.java @@ -44,7 +44,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.spanner.admin.database.v1.DatabaseAdminGrpc; -import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import com.google.spanner.v1.SpannerGrpc; import java.util.concurrent.Callable; @@ -270,11 +269,17 @@ public ApiFuture executeDdlAsync(final ParsedStatement ddl) { Callable callable = () -> { try { - OperationFuture operation = - ddlClient.executeDdl(ddl.getSqlWithoutComments()); - Void res = getWithStatementTimeout(operation, ddl); + OperationFuture operation; + if (DdlClient.isCreateDatabaseStatement(ddl.getSqlWithoutComments())) { + operation = + ddlClient.executeCreateDatabase( + ddl.getSqlWithoutComments(), dbClient.getDialect()); + } else { + operation = ddlClient.executeDdl(ddl.getSqlWithoutComments()); + } + getWithStatementTimeout(operation, ddl); state = UnitOfWorkState.COMMITTED; - return res; + return null; } catch (Throwable t) { state = UnitOfWorkState.COMMIT_FAILED; throw t; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlBatchTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlBatchTest.java index 23792572d79..2496adb270b 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlBatchTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlBatchTest.java @@ -22,6 +22,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.anyString; @@ -143,6 +144,17 @@ public void testExecuteQuery() { } } + @Test + public void testExecuteCreateDatabase() { + DdlBatch batch = createSubject(); + assertThrows( + IllegalArgumentException.class, + () -> + batch.executeDdlAsync( + AbstractStatementParser.getInstance(Dialect.GOOGLE_STANDARD_SQL) + .parse(Statement.of("CREATE DATABASE foo")))); + } + @Test public void testExecuteMetadataQuery() { Statement statement = Statement.of("SELECT * FROM INFORMATION_SCHEMA.TABLES"); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlClientTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlClientTest.java index 24b4ebf0964..a490949a31b 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlClientTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/DdlClientTest.java @@ -16,6 +16,8 @@ package com.google.cloud.spanner.connection; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.isNull; @@ -66,4 +68,23 @@ public void testExecuteDdl() throws InterruptedException, ExecutionException { subject.executeDdl(ddlList); verify(client).updateDatabaseDdl(instanceId, databaseId, ddlList, null); } + + @Test + public void testIsCreateDatabase() { + assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE foo")); + assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE \"foo\"")); + assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE `foo`")); + assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE\tfoo")); + assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE\n foo")); + assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE\t\n foo")); + assertTrue(DdlClient.isCreateDatabaseStatement("CREATE DATABASE")); + assertTrue(DdlClient.isCreateDatabaseStatement("CREATE\t \n DATABASE foo")); + assertTrue(DdlClient.isCreateDatabaseStatement("create\t \n DATABASE foo")); + assertTrue(DdlClient.isCreateDatabaseStatement("create database foo")); + + assertFalse(DdlClient.isCreateDatabaseStatement("CREATE VIEW foo")); + assertFalse(DdlClient.isCreateDatabaseStatement("CREATE DATABAS foo")); + assertFalse(DdlClient.isCreateDatabaseStatement("CREATE DATABASEfoo")); + assertFalse(DdlClient.isCreateDatabaseStatement("CREATE foo")); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java index 26a3952be28..67db5e2d8b4 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java @@ -34,6 +34,7 @@ import com.google.cloud.spanner.AsyncResultSet; import com.google.cloud.spanner.CommitResponse; import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeySet; @@ -365,6 +366,7 @@ private SingleUseTransaction createSubject( DatabaseClient dbClient = mock(DatabaseClient.class); com.google.cloud.spanner.ReadOnlyTransaction singleUse = new SimpleReadOnlyTransaction(staleness); + when(dbClient.getDialect()).thenReturn(Dialect.GOOGLE_STANDARD_SQL); when(dbClient.singleUseReadOnlyTransaction(staleness)).thenReturn(singleUse); final TransactionContext txContext = mock(TransactionContext.class); @@ -537,6 +539,19 @@ public void testExecuteDdl() { verify(ddlClient).executeDdl(sql); } + @Test + public void testExecuteCreateDatabase() { + String sql = "CREATE DATABASE FOO"; + ParsedStatement ddl = createParsedDdl(sql); + DdlClient ddlClient = createDefaultMockDdlClient(); + when(ddlClient.executeCreateDatabase(sql, Dialect.GOOGLE_STANDARD_SQL)) + .thenReturn(mock(OperationFuture.class)); + + SingleUseTransaction singleUseTransaction = createDdlSubject(ddlClient); + get(singleUseTransaction.executeDdlAsync(ddl)); + verify(ddlClient).executeCreateDatabase(sql, Dialect.GOOGLE_STANDARD_SQL); + } + @Test public void testExecuteQuery() { for (TimestampBound staleness : getTestTimestampBounds()) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITDdlTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITDdlTest.java index 74c072cd760..7a9c5aa9262 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITDdlTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/it/ITDdlTest.java @@ -16,7 +16,14 @@ package com.google.cloud.spanner.connection.it; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; + +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseNotFoundException; import com.google.cloud.spanner.ParallelIntegrationTest; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.connection.Connection; import com.google.cloud.spanner.connection.ITAbstractSpannerTest; import com.google.cloud.spanner.connection.SqlScriptVerifier; import org.junit.Test; @@ -34,4 +41,20 @@ public void testSqlScript() throws Exception { SqlScriptVerifier verifier = new SqlScriptVerifier(new ITConnectionProvider()); verifier.verifyStatementsInFile("ITDdlTest.sql", SqlScriptVerifier.class, false); } + + @Test + public void testCreateDatabase() { + DatabaseAdminClient client = getTestEnv().getTestHelper().getClient().getDatabaseAdminClient(); + String instance = getTestEnv().getTestHelper().getInstanceId().getInstance(); + String name = getTestEnv().getTestHelper().getUniqueDatabaseId(); + + assertThrows(DatabaseNotFoundException.class, () -> client.getDatabase(instance, name)); + + try (Connection connection = createConnection()) { + connection.execute(Statement.of(String.format("CREATE DATABASE `%s`", name))); + assertNotNull(client.getDatabase(instance, name)); + } finally { + client.dropDatabase(instance, name); + } + } } From ca85276efa6a71d6e5e7951e0483d4e0889e40f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 28 Apr 2022 15:14:12 +0200 Subject: [PATCH 15/24] chore: make DatabaseClient accessible in the Connection API (#1847) Makes the underlying `DatabaseClient` accessible in the Connection API so this can be used by drivers that need direct access to it. See https://github.com/GoogleCloudPlatform/pgadapter/blob/8f0d47785e04dc9db6ee275074777849f006d797/src/main/java/com/google/cloud/spanner/pgadapter/utils/MutationWriter.java#L439 --- google-cloud-spanner/clirr-ignored-differences.xml | 6 +++++- .../com/google/cloud/spanner/connection/Connection.java | 7 +++++++ .../google/cloud/spanner/connection/ConnectionImpl.java | 5 +++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 450eacde322..ef38a8cfa0f 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -60,10 +60,14 @@ com/google/cloud/spanner/spi/v1/SpannerRpc com.google.api.gax.longrunning.OperationFuture copyBackup(com.google.cloud.spanner.BackupId, com.google.cloud.spanner.Backup) + + 7012 + com/google/cloud/spanner/connection/Connection + com.google.cloud.spanner.DatabaseClient getDatabaseClient() + 7012 com/google/cloud/spanner/DatabaseAdminClient com.google.api.gax.longrunning.OperationFuture createDatabase(java.lang.String, java.lang.String, com.google.cloud.spanner.Dialect, java.lang.Iterable) - diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java index 9c74e5b9ca1..e3a5f7edbce 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/Connection.java @@ -23,6 +23,7 @@ import com.google.cloud.spanner.AbortedException; import com.google.cloud.spanner.AsyncResultSet; import com.google.cloud.spanner.CommitResponse; +import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.Mutation; @@ -1104,6 +1105,12 @@ default Dialect getDialect() { throw new UnsupportedOperationException("Not implemented"); } + /** The {@link DatabaseClient} that is used by this {@link Connection}. */ + @InternalApi + default DatabaseClient getDatabaseClient() { + throw new UnsupportedOperationException("Not implemented"); + } + /** * This query option is used internally to indicate that a query is executed by the library itself * to fetch metadata. These queries are specifically allowed to be executed even when a DDL batch diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java index 3f3bd218ef2..1a7d9b76682 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java @@ -332,6 +332,11 @@ public Dialect getDialect() { return dbClient.getDialect(); } + @Override + public DatabaseClient getDatabaseClient() { + return dbClient; + } + @Override public boolean isClosed() { return closed; From 182906f7a952991cf36c8401465c883fcc0f63e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 28 Apr 2022 16:13:05 +0200 Subject: [PATCH 16/24] chore: support untyped values for statement parameters (#1854) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: support untyped values * chore: support untyped values Adds support for untyped values that can be used as statement parameters. This is required for Spangres, as many native PG drivers send (some) parameters without an explicit type, and expect the backend to infer the type from the context. If we were to include a (placeholder) type for such a parameter, the type inference on the backend fails. * chore: support untyped values Adds support for untyped values that can be used as statement parameters. This is required for Spangres, as many native PG drivers send (some) parameters without an explicit type, and expect the backend to infer the type from the context. If we were to include a (placeholder) type for such a parameter, the type inference on the backend fails. * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot --- .../cloud/spanner/AbstractReadContext.java | 4 +- .../google/cloud/spanner/BatchClientImpl.java | 2 +- .../spanner/PartitionedDmlTransaction.java | 2 +- .../java/com/google/cloud/spanner/Value.java | 65 ++++++++++++++++++- .../google/cloud/spanner/ValueBinderTest.java | 7 +- .../com/google/cloud/spanner/ValueTest.java | 30 +++++++++ 6 files changed, 103 insertions(+), 7 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java index ea658a36eab..60324f8e83e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractReadContext.java @@ -584,7 +584,7 @@ ExecuteSqlRequest.Builder getExecuteSqlRequestBuilder( com.google.protobuf.Struct.Builder paramsBuilder = builder.getParamsBuilder(); for (Map.Entry param : stmtParameters.entrySet()) { paramsBuilder.putFields(param.getKey(), Value.toProto(param.getValue())); - if (param.getValue() != null) { + if (param.getValue() != null && param.getValue().getType() != null) { builder.putParamTypes(param.getKey(), param.getValue().getType().toProto()); } } @@ -615,7 +615,7 @@ ExecuteBatchDmlRequest.Builder getExecuteBatchDmlRequestBuilder( builder.getStatementsBuilder(idx).getParamsBuilder(); for (Map.Entry param : stmtParameters.entrySet()) { paramsBuilder.putFields(param.getKey(), Value.toProto(param.getValue())); - if (param.getValue() != null) { + if (param.getValue() != null && param.getValue().getType() != null) { builder .getStatementsBuilder(idx) .putParamTypes(param.getKey(), param.getValue().getType().toProto()); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java index 222d754dac9..5a164d52490 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/BatchClientImpl.java @@ -170,7 +170,7 @@ public List partitionQuery( Struct.Builder paramsBuilder = builder.getParamsBuilder(); for (Map.Entry param : stmtParameters.entrySet()) { paramsBuilder.putFields(param.getKey(), Value.toProto(param.getValue())); - if (param.getValue() != null) { + if (param.getValue() != null && param.getValue().getType() != null) { builder.putParamTypes(param.getKey(), param.getValue().getType().toProto()); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDmlTransaction.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDmlTransaction.java index 78c092f1792..cbe01c2d33d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDmlTransaction.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/PartitionedDmlTransaction.java @@ -218,7 +218,7 @@ private void setParameters( com.google.protobuf.Struct.Builder paramsBuilder = requestBuilder.getParamsBuilder(); for (Map.Entry param : statementParameters.entrySet()) { paramsBuilder.putFields(param.getKey(), Value.toProto(param.getValue())); - if (param.getValue() != null) { + if (param.getValue() != null && param.getValue().getType() != null) { requestBuilder.putParamTypes(param.getKey(), param.getValue().getType().toProto()); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java index 0bbeb36abf0..e3c53de9377 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Value.java @@ -87,6 +87,17 @@ public abstract class Value implements Serializable { private static final char LIST_CLOSE = ']'; private static final long serialVersionUID = -5289864325087675338L; + /** + * Returns a {@link Value} that wraps the given proto value. This can be used to construct a value + * without a specific type, and let the backend infer the type based on the statement where it is + * used. + * + * @param value the non-null proto value (a {@link NullValue} is allowed) + */ + public static Value untyped(com.google.protobuf.Value value) { + return new UntypedValueImpl(Preconditions.checkNotNull(value)); + } + /** * Returns a {@code BOOL} value. * @@ -914,7 +925,7 @@ public final boolean equals(Object o) { } AbstractValue that = (AbstractValue) o; - if (!getType().equals(that.getType()) || isNull != that.isNull) { + if (!Objects.equals(getType(), that.getType()) || isNull != that.isNull) { return false; } @@ -963,6 +974,58 @@ final void checkNotNull() { } } + private static class UntypedValueImpl extends AbstractValue { + private final com.google.protobuf.Value value; + + private UntypedValueImpl(com.google.protobuf.Value value) { + super(value.hasNullValue(), null); + this.value = value; + } + + @Override + public boolean getBool() { + checkNotNull(); + Preconditions.checkState(value.hasBoolValue(), "This value does not contain a bool value"); + return value.getBoolValue(); + } + + @Override + public String getString() { + checkNotNull(); + Preconditions.checkState( + value.hasStringValue(), "This value does not contain a string value"); + return value.getStringValue(); + } + + @Override + public double getFloat64() { + checkNotNull(); + Preconditions.checkState( + value.hasNumberValue(), "This value does not contain a number value"); + return value.getNumberValue(); + } + + @Override + void valueToString(StringBuilder b) { + b.append(value); + } + + @Override + com.google.protobuf.Value valueToProto() { + return value; + } + + @Override + boolean valueEquals(Value v) { + return ((UntypedValueImpl) v).value.equals(value); + } + + @Override + int valueHash() { + return value.hashCode(); + } + } + private static class BoolImpl extends AbstractValue { private final boolean value; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java index 122f5582736..ea26f09c2ed 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java @@ -56,10 +56,13 @@ Integer handle(Value value) { public void reflection() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { // Test that every Value factory method has a counterpart in ValueBinder, and that invoking it - // produces the expected Value. + // produces the expected Value. The only exception is for untyped values, which must be + // constructed manually as an untyped value and then assigned as a parameter. for (Method method : Value.class.getMethods()) { if (!Modifier.isStatic(method.getModifiers()) - || !method.getReturnType().equals(Value.class)) { + || !method.getReturnType().equals(Value.class) + || (method.getParameterTypes().length > 0 + && method.getParameterTypes()[0].equals(com.google.protobuf.Value.class))) { continue; } Method binderMethod = findBinderMethod(method); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java index 08849274de5..54f6799e8c8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java @@ -22,7 +22,9 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -63,6 +65,34 @@ private static Iterable plainIterable(T... values) { return Lists.newArrayList(values); } + @Test + public void untyped() { + com.google.protobuf.Value proto = + com.google.protobuf.Value.newBuilder().setStringValue("test").build(); + Value v = Value.untyped(proto); + assertNull(v.getType()); + assertFalse(v.isNull()); + assertSame(proto, v.toProto()); + + assertEquals( + v, Value.untyped(com.google.protobuf.Value.newBuilder().setStringValue("test").build())); + assertEquals( + Value.untyped(com.google.protobuf.Value.newBuilder().setNumberValue(3.14d).build()), + Value.untyped(com.google.protobuf.Value.newBuilder().setNumberValue(3.14d).build())); + assertEquals( + Value.untyped(com.google.protobuf.Value.newBuilder().setBoolValue(true).build()), + Value.untyped(com.google.protobuf.Value.newBuilder().setBoolValue(true).build())); + + assertNotEquals( + v, Value.untyped(com.google.protobuf.Value.newBuilder().setStringValue("foo").build())); + assertNotEquals( + Value.untyped(com.google.protobuf.Value.newBuilder().setNumberValue(3.14d).build()), + Value.untyped(com.google.protobuf.Value.newBuilder().setNumberValue(0.14d).build())); + assertNotEquals( + Value.untyped(com.google.protobuf.Value.newBuilder().setBoolValue(false).build()), + Value.untyped(com.google.protobuf.Value.newBuilder().setBoolValue(true).build())); + } + @Test public void bool() { Value v = Value.bool(true); From 6e1aa8e755886d4f9c342d3d445be3e5bc603416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 28 Apr 2022 16:20:36 +0200 Subject: [PATCH 17/24] chore: include client statement type in parsed statement (#1849) Include the ClientSideStatementType in ParsedStatement. This enables clients to directly check what type of statement it is, which removes the need for textual checks. This will for example remove these types of checks: https://github.com/GoogleCloudPlatform/pgadapter/blob/2a3f2eb7cd16cc3c71ffc673544ca9e2baaba21c/src/main/java/com/google/cloud/spanner/pgadapter/statements/IntermediateStatement.java#L354 --- .../connection/AbstractStatementParser.java | 19 +++++++++- .../connection/ClientSideStatement.java | 4 ++ .../connection/ClientSideStatementImpl.java | 8 ++++ .../connection/ClientSideStatements.json | 35 ++++++++++++++++++ .../connection/PG_ClientSideStatements.json | 37 +++++++++++++++++++ .../connection/ClientSideStatementsTest.java | 25 +++++++++++++ 6 files changed, 127 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java index 25465990d2f..1465b8b4021 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java @@ -22,11 +22,13 @@ import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -270,6 +272,16 @@ public boolean isDdl() { return false; } + /** + * Returns the {@link ClientSideStatementType} of this statement. This method may only be called + * on statements of type {@link StatementType#CLIENT_SIDE}. + */ + @InternalApi + public ClientSideStatementType getClientSideStatementType() { + Preconditions.checkState(type == StatementType.CLIENT_SIDE); + return clientSideStatement.getStatementType(); + } + Statement getStatement() { return statement; } @@ -314,7 +326,12 @@ ClientSideStatement getClientSideStatement() { private final Set statements; AbstractStatementParser(Set statements) { - this.statements = statements; + this.statements = Collections.unmodifiableSet(statements); + } + + @VisibleForTesting + Set getClientSideStatements() { + return statements; } /** diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatement.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatement.java index f3c3691b96b..08f14b146a2 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatement.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatement.java @@ -17,6 +17,7 @@ package com.google.cloud.spanner.connection; import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType; import java.util.List; /** @@ -47,6 +48,9 @@ interface ClientSideStatement { /** @return true if this {@link ClientSideStatement} will return an update count. */ boolean isUpdate(); + /** @return the statement type */ + ClientSideStatementType getStatementType(); + /** * Execute this {@link ClientSideStatement} on the given {@link ConnectionStatementExecutor}. The * executor calls the appropriate method(s) on the {@link Connection}. The statement argument is diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementImpl.java index 05dbc8975ce..2ff39131baa 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ClientSideStatementImpl.java @@ -17,6 +17,7 @@ package com.google.cloud.spanner.connection; import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType; import com.google.cloud.spanner.connection.StatementResult.ResultType; import com.google.common.base.Preconditions; import java.lang.reflect.Constructor; @@ -106,6 +107,8 @@ public String getMessage() { /** The result type of this statement. */ private ResultType resultType; + private ClientSideStatementType statementType; + /** The regular expression that should be used to recognize this class of statements. */ private String regex; @@ -183,6 +186,11 @@ public boolean isUpdate() { return resultType == ResultType.UPDATE_COUNT; } + @Override + public ClientSideStatementType getStatementType() { + return statementType; + } + boolean matches(String statement) { Preconditions.checkState(pattern != null, "This statement has not been compiled"); return pattern.matcher(statement).matches(); diff --git a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json index e06732bf4e8..860c0238e06 100644 --- a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json +++ b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/ClientSideStatements.json @@ -5,6 +5,7 @@ "name": "SHOW VARIABLE AUTOCOMMIT", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_AUTOCOMMIT", "regex": "(?is)\\A\\s*show\\s+variable\\s+autocommit\\s*\\z", "method": "statementShowAutocommit", "exampleStatements": ["show variable autocommit"] @@ -13,6 +14,7 @@ "name": "SHOW VARIABLE READONLY", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_READONLY", "regex": "(?is)\\A\\s*show\\s+variable\\s+readonly\\s*\\z", "method": "statementShowReadOnly", "exampleStatements": ["show variable readonly"] @@ -21,6 +23,7 @@ "name": "SHOW VARIABLE RETRY_ABORTS_INTERNALLY", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_RETRY_ABORTS_INTERNALLY", "regex": "(?is)\\A\\s*show\\s+variable\\s+retry_aborts_internally\\s*\\z", "method": "statementShowRetryAbortsInternally", "exampleStatements": ["show variable retry_aborts_internally"], @@ -30,6 +33,7 @@ "name": "SHOW VARIABLE AUTOCOMMIT_DML_MODE", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_AUTOCOMMIT_DML_MODE", "regex": "(?is)\\A\\s*show\\s+variable\\s+autocommit_dml_mode\\s*\\z", "method": "statementShowAutocommitDmlMode", "exampleStatements": ["show variable autocommit_dml_mode"] @@ -38,6 +42,7 @@ "name": "SHOW VARIABLE STATEMENT_TIMEOUT", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_STATEMENT_TIMEOUT", "regex": "(?is)\\A\\s*show\\s+variable\\s+statement_timeout\\s*\\z", "method": "statementShowStatementTimeout", "exampleStatements": ["show variable statement_timeout"] @@ -46,6 +51,7 @@ "name": "SHOW VARIABLE READ_TIMESTAMP", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_READ_TIMESTAMP", "regex": "(?is)\\A\\s*show\\s+variable\\s+read_timestamp\\s*\\z", "method": "statementShowReadTimestamp", "exampleStatements": ["show variable read_timestamp"], @@ -55,6 +61,7 @@ "name": "SHOW VARIABLE COMMIT_TIMESTAMP", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_COMMIT_TIMESTAMP", "regex": "(?is)\\A\\s*show\\s+variable\\s+commit_timestamp\\s*\\z", "method": "statementShowCommitTimestamp", "exampleStatements": ["show variable commit_timestamp"], @@ -64,6 +71,7 @@ "name": "SHOW VARIABLE READ_ONLY_STALENESS", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_READ_ONLY_STALENESS", "regex": "(?is)\\A\\s*show\\s+variable\\s+read_only_staleness\\s*\\z", "method": "statementShowReadOnlyStaleness", "exampleStatements": ["show variable read_only_staleness"] @@ -72,6 +80,7 @@ "name": "SHOW VARIABLE OPTIMIZER_VERSION", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_OPTIMIZER_VERSION", "regex": "(?is)\\A\\s*show\\s+variable\\s+optimizer_version\\s*\\z", "method": "statementShowOptimizerVersion", "exampleStatements": ["show variable optimizer_version"] @@ -80,6 +89,7 @@ "name": "SHOW VARIABLE OPTIMIZER_STATISTICS_PACKAGE", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_OPTIMIZER_STATISTICS_PACKAGE", "regex": "(?is)\\A\\s*show\\s+variable\\s+optimizer_statistics_package\\s*\\z", "method": "statementShowOptimizerStatisticsPackage", "exampleStatements": ["show variable optimizer_statistics_package"] @@ -88,6 +98,7 @@ "name": "SHOW VARIABLE RETURN_COMMIT_STATS", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_RETURN_COMMIT_STATS", "regex": "(?is)\\A\\s*show\\s+variable\\s+return_commit_stats\\s*\\z", "method": "statementShowReturnCommitStats", "exampleStatements": ["show variable return_commit_stats"] @@ -96,6 +107,7 @@ "name": "SHOW VARIABLE COMMIT_RESPONSE", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_COMMIT_RESPONSE", "regex": "(?is)\\A\\s*show\\s+variable\\s+commit_response\\s*\\z", "method": "statementShowCommitResponse", "exampleStatements": ["show variable commit_response"], @@ -105,6 +117,7 @@ "name": "SHOW VARIABLE STATEMENT_TAG", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_STATEMENT_TAG", "regex": "(?is)\\A\\s*show\\s+variable\\s+statement_tag\\s*\\z", "method": "statementShowStatementTag", "exampleStatements": ["show variable statement_tag"] @@ -113,6 +126,7 @@ "name": "SHOW VARIABLE TRANSACTION_TAG", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_TRANSACTION_TAG", "regex": "(?is)\\A\\s*show\\s+variable\\s+transaction_tag\\s*\\z", "method": "statementShowTransactionTag", "exampleStatements": ["show variable transaction_tag"] @@ -121,6 +135,7 @@ "name": "SHOW VARIABLE RPC_PRIORITY", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_TRANSACTION_TAG", "regex": "(?is)\\A\\s*show\\s+variable\\s+rpc_priority\\s*\\z", "method": "statementShowRPCPriority", "exampleStatements": ["show variable rpc_priority"] @@ -129,6 +144,7 @@ "name": "BEGIN TRANSACTION", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "BEGIN", "regex": "(?is)\\A\\s*(?:begin|start)(?:\\s+transaction)?\\s*\\z", "method": "statementBeginTransaction", "exampleStatements": ["begin", "start", "begin transaction", "start transaction"] @@ -137,6 +153,7 @@ "name": "COMMIT TRANSACTION", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "COMMIT", "regex": "(?is)\\A\\s*(?:commit)(?:\\s+transaction)?\\s*\\z", "method": "statementCommit", "exampleStatements": ["commit", "commit transaction"], @@ -146,6 +163,7 @@ "name": "ROLLBACK TRANSACTION", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "ROLLBACK", "regex": "(?is)\\A\\s*(?:rollback)(?:\\s+transaction)?\\s*\\z", "method": "statementRollback", "exampleStatements": ["rollback", "rollback transaction"], @@ -155,6 +173,7 @@ "name": "START BATCH DDL", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "START_BATCH_DDL", "regex": "(?is)\\A\\s*(?:start)(?:\\s+batch)(?:\\s+ddl)\\s*\\z", "method": "statementStartBatchDdl", "exampleStatements": ["start batch ddl"] @@ -163,6 +182,7 @@ "name": "START BATCH DML", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "START_BATCH_DML", "regex": "(?is)\\A\\s*(?:start)(?:\\s+batch)(?:\\s+dml)\\s*\\z", "method": "statementStartBatchDml", "exampleStatements": ["start batch dml"] @@ -171,6 +191,7 @@ "name": "RUN BATCH", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "RUN_BATCH", "regex": "(?is)\\A\\s*(?:run)(?:\\s+batch)\\s*\\z", "method": "statementRunBatch", "exampleStatements": ["run batch"], @@ -180,6 +201,7 @@ "name": "ABORT BATCH", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "ABORT_BATCH", "regex": "(?is)\\A\\s*(?:abort)(?:\\s+batch)\\s*\\z", "method": "statementAbortBatch", "exampleStatements": ["abort batch"], @@ -189,6 +211,7 @@ "name": "SET AUTOCOMMIT = TRUE|FALSE", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_AUTOCOMMIT", "regex": "(?is)\\A\\s*set\\s+autocommit\\s*(?:=)\\s*(.*)\\z", "method": "statementSetAutocommit", "exampleStatements": ["set autocommit = true", "set autocommit = false"], @@ -203,6 +226,7 @@ "name": "SET READONLY = TRUE|FALSE", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_READONLY", "regex": "(?is)\\A\\s*set\\s+readonly\\s*(?:=)\\s*(.*)\\z", "method": "statementSetReadOnly", "exampleStatements": ["set readonly = true", "set readonly = false"], @@ -217,6 +241,7 @@ "name": "SET RETRY_ABORTS_INTERNALLY = TRUE|FALSE", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_RETRY_ABORTS_INTERNALLY", "regex": "(?is)\\A\\s*set\\s+retry_aborts_internally\\s*(?:=)\\s*(.*)\\z", "method": "statementSetRetryAbortsInternally", "exampleStatements": ["set retry_aborts_internally = true", "set retry_aborts_internally = false"], @@ -232,6 +257,7 @@ "name": "SET AUTOCOMMIT_DML_MODE = 'PARTITIONED_NON_ATOMIC'|'TRANSACTIONAL'", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_AUTOCOMMIT_DML_MODE", "regex": "(?is)\\A\\s*set\\s+autocommit_dml_mode\\s*(?:=)\\s*(.*)\\z", "method": "statementSetAutocommitDmlMode", "exampleStatements": ["set autocommit_dml_mode='PARTITIONED_NON_ATOMIC'", "set autocommit_dml_mode='TRANSACTIONAL'"], @@ -246,6 +272,7 @@ "name": "SET STATEMENT_TIMEOUT = ''|NULL", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_STATEMENT_TIMEOUT", "regex": "(?is)\\A\\s*set\\s+statement_timeout\\s*(?:=)\\s*(.*)\\z", "method": "statementSetStatementTimeout", "exampleStatements": ["set statement_timeout=null", "set statement_timeout='1s'", "set statement_timeout='100ms'", "set statement_timeout='10000us'", "set statement_timeout='9223372036854775807ns'"], @@ -260,6 +287,7 @@ "name": "SET TRANSACTION READ ONLY|READ WRITE", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_TRANSACTION_MODE", "regex": "(?is)\\A\\s*set\\s+transaction\\s*(?:\\s+)\\s*(.*)\\z", "method": "statementSetTransactionMode", "exampleStatements": ["set transaction read only", "set transaction read write"], @@ -275,6 +303,7 @@ "name": "SET READ_ONLY_STALENESS = 'STRONG' | 'MIN_READ_TIMESTAMP ' | 'READ_TIMESTAMP ' | 'MAX_STALENESS s|ms|us|ns' | 'EXACT_STALENESS (s|ms|us|ns)'", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_READ_ONLY_STALENESS", "regex": "(?is)\\A\\s*set\\s+read_only_staleness\\s*(?:=)\\s*(.*)\\z", "method": "statementSetReadOnlyStaleness", "exampleStatements": ["set read_only_staleness='STRONG'", @@ -303,6 +332,7 @@ "name": "SET OPTIMIZER_VERSION = ''|'LATEST'|''", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_OPTIMIZER_VERSION", "regex": "(?is)\\A\\s*set\\s+optimizer_version\\s*(?:=)\\s*(.*)\\z", "method": "statementSetOptimizerVersion", "exampleStatements": ["set optimizer_version='1'", "set optimizer_version='200'", "set optimizer_version='LATEST'", "set optimizer_version=''"], @@ -317,6 +347,7 @@ "name": "SET OPTIMIZER_STATISTICS_PACKAGE = ''|''", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_OPTIMIZER_STATISTICS_PACKAGE", "regex": "(?is)\\A\\s*set\\s+optimizer_statistics_package\\s*(?:=)\\s*(.*)\\z", "method": "statementSetOptimizerStatisticsPackage", "exampleStatements": ["set optimizer_statistics_package='auto_20191128_14_47_22UTC'", "set optimizer_statistics_package=''"], @@ -331,6 +362,7 @@ "name": "SET RETURN_COMMIT_STATS = TRUE|FALSE", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_RETURN_COMMIT_STATS", "regex": "(?is)\\A\\s*set\\s+return_commit_stats\\s*(?:=)\\s*(.*)\\z", "method": "statementSetReturnCommitStats", "exampleStatements": ["set return_commit_stats = true", "set return_commit_stats = false"], @@ -345,6 +377,7 @@ "name": "SET STATEMENT_TAG = ''", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_STATEMENT_TAG", "regex": "(?is)\\A\\s*set\\s+statement_tag\\s*(?:=)\\s*(.*)\\z", "method": "statementSetStatementTag", "exampleStatements": ["set statement_tag='tag1'", "set statement_tag='tag2'", "set statement_tag=''"], @@ -359,6 +392,7 @@ "name": "SET TRANSACTION_TAG = ''", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_TRANSACTION_TAG", "regex": "(?is)\\A\\s*set\\s+transaction_tag\\s*(?:=)\\s*(.*)\\z", "method": "statementSetTransactionTag", "exampleStatements": ["set transaction_tag='tag1'", "set transaction_tag='tag2'", "set transaction_tag=''"], @@ -374,6 +408,7 @@ "name": "SET RPC_PRIORITY = 'HIGH'|'MEDIUM'|'LOW'|'NULL'", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_RPC_PRIORITY", "regex": "(?is)\\A\\s*set\\s+rpc_priority\\s*(?:=)\\s*(.*)\\z", "method": "statementSetRPCPriority", "exampleStatements": [ diff --git a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/PG_ClientSideStatements.json b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/PG_ClientSideStatements.json index 31e3a801005..281fc4792d4 100644 --- a/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/PG_ClientSideStatements.json +++ b/google-cloud-spanner/src/main/resources/com/google/cloud/spanner/connection/PG_ClientSideStatements.json @@ -5,6 +5,7 @@ "name": "SHOW [VARIABLE] AUTOCOMMIT", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_AUTOCOMMIT", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?autocommit\\s*\\z", "method": "statementShowAutocommit", "exampleStatements": ["show autocommit","show variable autocommit"] @@ -13,6 +14,7 @@ "name": "SHOW [VARIABLE] SPANNER.READONLY", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_READONLY", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.readonly\\s*\\z", "method": "statementShowReadOnly", "exampleStatements": ["show spanner.readonly","show variable spanner.readonly"] @@ -21,6 +23,7 @@ "name": "SHOW [VARIABLE] SPANNER.RETRY_ABORTS_INTERNALLY", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_RETRY_ABORTS_INTERNALLY", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.retry_aborts_internally\\s*\\z", "method": "statementShowRetryAbortsInternally", "exampleStatements": ["show spanner.retry_aborts_internally","show variable spanner.retry_aborts_internally"], @@ -30,6 +33,7 @@ "name": "SHOW [VARIABLE] SPANNER.AUTOCOMMIT_DML_MODE", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_AUTOCOMMIT_DML_MODE", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.autocommit_dml_mode\\s*\\z", "method": "statementShowAutocommitDmlMode", "exampleStatements": ["show spanner.autocommit_dml_mode","show variable spanner.autocommit_dml_mode"] @@ -38,6 +42,7 @@ "name": "SHOW [VARIABLE] STATEMENT_TIMEOUT", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_STATEMENT_TIMEOUT", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?statement_timeout\\s*\\z", "method": "statementShowStatementTimeout", "exampleStatements": ["show statement_timeout","show variable statement_timeout"] @@ -46,6 +51,7 @@ "name": "SHOW [VARIABLE] SPANNER.READ_TIMESTAMP", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_READ_TIMESTAMP", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.read_timestamp\\s*\\z", "method": "statementShowReadTimestamp", "exampleStatements": ["show spanner.read_timestamp","show variable spanner.read_timestamp"], @@ -55,6 +61,7 @@ "name": "SHOW [VARIABLE] SPANNER.COMMIT_TIMESTAMP", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_COMMIT_TIMESTAMP", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.commit_timestamp\\s*\\z", "method": "statementShowCommitTimestamp", "exampleStatements": ["show spanner.commit_timestamp","show variable spanner.commit_timestamp"], @@ -64,6 +71,7 @@ "name": "SHOW [VARIABLE] SPANNER.READ_ONLY_STALENESS", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_READ_ONLY_STALENESS", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.read_only_staleness\\s*\\z", "method": "statementShowReadOnlyStaleness", "exampleStatements": ["show spanner.read_only_staleness","show variable spanner.read_only_staleness"] @@ -72,6 +80,7 @@ "name": "SHOW [VARIABLE] SPANNER.OPTIMIZER_VERSION", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_OPTIMIZER_VERSION", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.optimizer_version\\s*\\z", "method": "statementShowOptimizerVersion", "exampleStatements": ["show spanner.optimizer_version","show variable spanner.optimizer_version"] @@ -80,6 +89,7 @@ "name": "SHOW [VARIABLE] SPANNER.OPTIMIZER_STATISTICS_PACKAGE", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_OPTIMIZER_STATISTICS_PACKAGE", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.optimizer_statistics_package\\s*\\z", "method": "statementShowOptimizerStatisticsPackage", "exampleStatements": ["show spanner.optimizer_statistics_package","show variable spanner.optimizer_statistics_package"] @@ -88,6 +98,7 @@ "name": "SHOW [VARIABLE] SPANNER.RETURN_COMMIT_STATS", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_RETURN_COMMIT_STATS", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.return_commit_stats\\s*\\z", "method": "statementShowReturnCommitStats", "exampleStatements": ["show spanner.return_commit_stats","show variable spanner.return_commit_stats"] @@ -96,6 +107,7 @@ "name": "SHOW [VARIABLE] SPANNER.COMMIT_RESPONSE", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_COMMIT_RESPONSE", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.commit_response\\s*\\z", "method": "statementShowCommitResponse", "exampleStatements": ["show spanner.commit_response","show variable spanner.commit_response"], @@ -105,6 +117,7 @@ "name": "SHOW [VARIABLE] SPANNER.STATEMENT_TAG", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_STATEMENT_TAG", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.statement_tag\\s*\\z", "method": "statementShowStatementTag", "exampleStatements": ["show spanner.statement_tag","show variable spanner.statement_tag"] @@ -113,6 +126,7 @@ "name": "SHOW [VARIABLE] SPANNER.TRANSACTION_TAG", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_TRANSACTION_TAG", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.transaction_tag\\s*\\z", "method": "statementShowTransactionTag", "exampleStatements": ["show spanner.transaction_tag","show variable spanner.transaction_tag"] @@ -121,6 +135,7 @@ "name": "SHOW [VARIABLE] SPANNER.RPC_PRIORITY", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_RPC_PRIORITY", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?spanner\\.rpc_priority\\s*\\z", "method": "statementShowRPCPriority", "exampleStatements": ["show spanner.rpc_priority","show variable spanner.rpc_priority"] @@ -129,6 +144,7 @@ "name": "SHOW [VARIABLE] TRANSACTION ISOLATION LEVEL", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "RESULT_SET", + "statementType": "SHOW_TRANSACTION_ISOLATION_LEVEL", "regex": "(?is)\\A\\s*show\\s+(?:variable\\s+)?transaction\\s*isolation\\s*level\\s*\\z", "method": "statementShowTransactionIsolationLevel", "exampleStatements": ["show transaction isolation level","show variable transaction isolation level"] @@ -137,6 +153,7 @@ "name": "{START | BEGIN} [TRANSACTION | WORK] [{ (READ ONLY|READ WRITE) | (ISOLATION LEVEL (DEFAULT|SERIALIZABLE)) }]", "executorName": "ClientSideStatementPgBeginExecutor", "resultType": "NO_RESULT", + "statementType": "BEGIN", "regex": "(?is)\\A\\s*(?:begin|start)(?:\\s+transaction|\\s+work)?(\\s+read\\s+only|\\s+read\\s+write|\\s+isolation\\s+level\\s+default|\\s+isolation\\s+level\\s+serializable)?\\s*\\z", "method": "statementBeginPgTransaction", "exampleStatements": [ @@ -151,6 +168,7 @@ "name": "COMMIT [TRANSACTION | WORK] [AND NO CHAIN]", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "COMMIT", "regex": "(?is)\\A\\s*(?:commit)(?:\\s+transaction|\\s+work)?(?:\\s+and\\s+no\\s+chain)?\\s*\\z", "method": "statementCommit", "exampleStatements": ["commit", "commit transaction", "commit work", "commit and no chain", "commit transaction and no chain", "commit work and no chain"], @@ -160,6 +178,7 @@ "name": "ROLLBACK [TRANSACTION | WORK] [AND NO CHAIN]", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "ROLLBACK", "regex": "(?is)\\A\\s*(?:rollback)(?:\\s+transaction|\\s+work)?(?:\\s+and\\s+no\\s+chain)?\\s*\\z", "method": "statementRollback", "exampleStatements": ["rollback", "rollback transaction", "rollback work", "rollback and no chain", "rollback transaction and no chain", "rollback work and no chain"], @@ -169,6 +188,7 @@ "name": "START BATCH DDL", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "START_BATCH_DDL", "regex": "(?is)\\A\\s*(?:start)(?:\\s+batch)(?:\\s+ddl)\\s*\\z", "method": "statementStartBatchDdl", "exampleStatements": ["start batch ddl"] @@ -177,6 +197,7 @@ "name": "START BATCH DML", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "START_BATCH_DML", "regex": "(?is)\\A\\s*(?:start)(?:\\s+batch)(?:\\s+dml)\\s*\\z", "method": "statementStartBatchDml", "exampleStatements": ["start batch dml"] @@ -185,6 +206,7 @@ "name": "RUN BATCH", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "RUN_BATCH", "regex": "(?is)\\A\\s*(?:run)(?:\\s+batch)\\s*\\z", "method": "statementRunBatch", "exampleStatements": ["run batch"], @@ -194,6 +216,7 @@ "name": "ABORT BATCH", "executorName": "ClientSideStatementNoParamExecutor", "resultType": "NO_RESULT", + "statementType": "ABORT_BATCH", "regex": "(?is)\\A\\s*(?:abort)(?:\\s+batch)\\s*\\z", "method": "statementAbortBatch", "exampleStatements": ["abort batch"], @@ -203,6 +226,7 @@ "name": "SET AUTOCOMMIT =|TO TRUE|FALSE", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_AUTOCOMMIT", "regex": "(?is)\\A\\s*set\\s+autocommit(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetAutocommit", "exampleStatements": ["set autocommit = true", "set autocommit = false", "set autocommit to true", "set autocommit to false"], @@ -217,6 +241,7 @@ "name": "SET SPANNER.READONLY =|TO TRUE|FALSE", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_READONLY", "regex": "(?is)\\A\\s*set\\s+spanner\\.readonly(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetReadOnly", "exampleStatements": ["set spanner.readonly = true", "set spanner.readonly = false", "set spanner.readonly to true", "set spanner.readonly to false"], @@ -231,6 +256,7 @@ "name": "SET SPANNER.RETRY_ABORTS_INTERNALLY =|TO TRUE|FALSE", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_RETRY_ABORTS_INTERNALLY", "regex": "(?is)\\A\\s*set\\s+spanner\\.retry_aborts_internally(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetRetryAbortsInternally", "exampleStatements": ["set spanner.retry_aborts_internally = true", "set spanner.retry_aborts_internally = false", "set spanner.retry_aborts_internally to true", "set spanner.retry_aborts_internally to false"], @@ -246,6 +272,7 @@ "name": "SET SPANNER.AUTOCOMMIT_DML_MODE =|TO 'PARTITIONED_NON_ATOMIC'|'TRANSACTIONAL'", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_AUTOCOMMIT_DML_MODE", "regex": "(?is)\\A\\s*set\\s+spanner\\.autocommit_dml_mode(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetAutocommitDmlMode", "exampleStatements": [ @@ -265,6 +292,7 @@ "name": "SET STATEMENT_TIMEOUT =|TO ''|INT8|DEFAULT", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_STATEMENT_TIMEOUT", "regex": "(?is)\\A\\s*set\\s+statement_timeout(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetStatementTimeout", "exampleStatements": [ @@ -292,6 +320,7 @@ "name": "SET TRANSACTION { (READ ONLY|READ WRITE) | (ISOLATION LEVEL (DEFAULT|SERIALIZABLE)) }", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_TRANSACTION_MODE", "regex": "(?is)\\A\\s*set\\s+transaction\\s*(?:\\s+)\\s*(.*)\\z", "method": "statementSetPgTransactionMode", "exampleStatements": ["set transaction read only", "set transaction read write", "set transaction isolation level default", "set transaction isolation level serializable"], @@ -307,6 +336,7 @@ "name": "SET SESSION CHARACTERISTICS AS TRANSACTION { (READ ONLY|READ WRITE) | (ISOLATION LEVEL (DEFAULT|SERIALIZABLE)) }", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_READONLY", "regex": "(?is)\\A\\s*set\\s+session\\s+characteristics\\s+as\\s+transaction\\s*(?:\\s+)\\s*(.*)\\z", "method": "statementSetPgSessionCharacteristicsTransactionMode", "exampleStatements": ["set session characteristics as transaction read only", "set session characteristics as transaction read write", "set session characteristics as transaction isolation level default", "set session characteristics as transaction isolation level serializable"], @@ -321,6 +351,7 @@ "name": "SET SPANNER.READ_ONLY_STALENESS =|TO 'STRONG' | 'MIN_READ_TIMESTAMP ' | 'READ_TIMESTAMP ' | 'MAX_STALENESS s|ms|us|ns' | 'EXACT_STALENESS (s|ms|us|ns)'", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_READ_ONLY_STALENESS", "regex": "(?is)\\A\\s*set\\s+spanner\\.read_only_staleness(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetReadOnlyStaleness", "exampleStatements": [ @@ -366,6 +397,7 @@ "name": "SET SPANNER.OPTIMIZER_VERSION =|TO ''|'LATEST'|''", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_OPTIMIZER_VERSION", "regex": "(?is)\\A\\s*set\\s+spanner\\.optimizer_version(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetOptimizerVersion", "exampleStatements": [ @@ -389,6 +421,7 @@ "name": "SET SPANNER.OPTIMIZER_STATISTICS_PACKAGE =|TO ''|''", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_OPTIMIZER_STATISTICS_PACKAGE", "regex": "(?is)\\A\\s*set\\s+spanner\\.optimizer_statistics_package(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetOptimizerStatisticsPackage", "exampleStatements": [ @@ -408,6 +441,7 @@ "name": "SET SPANNER.RETURN_COMMIT_STATS =|TO TRUE|FALSE", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_RETURN_COMMIT_STATS", "regex": "(?is)\\A\\s*set\\s+spanner\\.return_commit_stats(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetReturnCommitStats", "exampleStatements": ["set spanner.return_commit_stats = true", "set spanner.return_commit_stats = false", "set spanner.return_commit_stats to true", "set spanner.return_commit_stats to false"], @@ -422,6 +456,7 @@ "name": "SET SPANNER.STATEMENT_TAG =|TO ''", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_STATEMENT_TAG", "regex": "(?is)\\A\\s*set\\s+spanner\\.statement_tag(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetStatementTag", "exampleStatements": [ @@ -443,6 +478,7 @@ "name": "SET SPANNER.TRANSACTION_TAG =|TO ''", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_TRANSACTION_TAG", "regex": "(?is)\\A\\s*set\\s+spanner\\.transaction_tag(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetTransactionTag", "exampleStatements": [ @@ -465,6 +501,7 @@ "name": "SET SPANNER.RPC_PRIORITY =|TO 'HIGH'|'MEDIUM'|'LOW'|'NULL'", "executorName": "ClientSideStatementSetExecutor", "resultType": "NO_RESULT", + "statementType": "SET_RPC_PRIORITY", "regex": "(?is)\\A\\s*set\\s+spanner\\.rpc_priority(?:\\s*=\\s*|\\s+to\\s+)(.*)\\z", "method": "statementSetRPCPriority", "exampleStatements": [ diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java index caf73f02271..c0ca4f96c9c 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ClientSideStatementsTest.java @@ -16,9 +16,14 @@ package com.google.cloud.spanner.connection; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + import com.google.cloud.spanner.Dialect; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; @@ -60,6 +65,26 @@ public void testExecuteClientSideStatementsScript() throws Exception { verifier.verifyStatementsInFile(getScriptFile(dialect), getClass(), true); } + @Test + public void testClientSideStatementType() { + AbstractStatementParser parser = AbstractStatementParser.getInstance(dialect); + + assertEquals( + ClientSideStatementType.BEGIN, + parser.parse(Statement.of("BEGIN TRANSACTION")).getClientSideStatementType()); + assertEquals( + ClientSideStatementType.COMMIT, + parser.parse(Statement.of("COMMIT TRANSACTION")).getClientSideStatementType()); + assertEquals( + ClientSideStatementType.ROLLBACK, + parser.parse(Statement.of("ROLLBACK TRANSACTION")).getClientSideStatementType()); + + for (ClientSideStatementImpl statement : parser.getClientSideStatements()) { + assertNotNull( + statement.toString() + " misses a statement type", statement.getStatementType()); + } + } + private static PrintWriter writer; /** Generates the test script file */ From 2471d9164ff1d7e0ba7987af321deba499938611 Mon Sep 17 00:00:00 2001 From: Rajat Bhatta <93644539+rajatbhatta@users.noreply.github.com> Date: Fri, 29 Apr 2022 13:25:23 +0530 Subject: [PATCH 18/24] test: drop old databases from instance before running integration tests. (#1861) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit aims to resolve the RESOURCE_EXHAUSTED errors due to number of databases in an instance reaching the maximum limit. We drop only those DBs which are created through automated tests, at least 24 hours ago. Co-authored-by: Knut Olav LΓΈite --- .../cloud/spanner/IntegrationTestEnv.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java index 3f4db8b9135..3de9f5358aa 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/IntegrationTestEnv.java @@ -19,12 +19,15 @@ import static com.google.common.base.Preconditions.checkState; import com.google.api.gax.longrunning.OperationFuture; +import com.google.api.gax.paging.Page; +import com.google.cloud.Timestamp; import com.google.cloud.spanner.testing.EmulatorSpannerHelper; import com.google.cloud.spanner.testing.RemoteSpannerHelper; import com.google.common.collect.Iterators; import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; import io.grpc.Status; import java.util.Random; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import org.junit.rules.ExternalResource; @@ -53,6 +56,7 @@ public class IntegrationTestEnv extends ExternalResource { private TestEnvConfig config; private InstanceAdminClient instanceAdminClient; + private DatabaseAdminClient databaseAdminClient; private boolean isOwnedInstance; private RemoteSpannerHelper testHelper; @@ -93,9 +97,12 @@ protected void before() throws Throwable { } testHelper = createTestHelper(options, instanceId); instanceAdminClient = testHelper.getClient().getInstanceAdminClient(); + databaseAdminClient = testHelper.getClient().getDatabaseAdminClient(); logger.log(Level.FINE, "Test env endpoint is {0}", options.getHost()); if (isOwnedInstance) { initializeInstance(instanceId); + } else { + cleanUpOldDatabases(instanceId); } } @@ -162,6 +169,35 @@ private void initializeInstance(InstanceId instanceId) { logger.log(Level.INFO, "Created test instance: {0}", createdInstance.getId()); } + private void cleanUpOldDatabases(InstanceId instanceId) { + long OLD_DB_THRESHOLD_SECS = TimeUnit.SECONDS.convert(24L, TimeUnit.HOURS); + Timestamp currentTimestamp = Timestamp.now(); + int numDropped = 0; + Page page = databaseAdminClient.listDatabases(instanceId.getInstance()); + String TEST_DB_REGEX = "(testdb_(.*)_(.*))|(mysample-(.*))"; + + logger.log(Level.INFO, "Dropping old test databases from {0}", instanceId.getName()); + while (page != null) { + for (Database db : page.iterateAll()) { + try { + long timeDiff = db.getCreateTime().getSeconds() - currentTimestamp.getSeconds(); + // Delete all databases which are more than OLD_DB_THRESHOLD_SECS seconds + // old. + if ((db.getId().getDatabase().matches(TEST_DB_REGEX)) + && (timeDiff > OLD_DB_THRESHOLD_SECS)) { + logger.log(Level.INFO, "Dropping test database {0}", db.getId()); + db.drop(); + ++numDropped; + } + } catch (SpannerException e) { + logger.log(Level.SEVERE, "Failed to drop test database " + db.getId(), e); + } + } + page = page.getNextPage(); + } + logger.log(Level.INFO, "Dropped {0} test database(s)", numDropped); + } + private void cleanUpInstance() { try { if (isOwnedInstance) { From e8322986f158a86cdbb04332a9c49ead79fb2587 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Fri, 29 Apr 2022 22:48:58 +0530 Subject: [PATCH 19/24] docs: add samples for PostgresSQL (#1781) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added samples for PG dialect databases * docs: add samples for postgresql * docs: samples tag fixes * docs: samples tag fixes * docs: samples tag violation fixes * docs: samples imports and indent fixes * docs: samples checkstyle fixes * docs: add samples for postgresql * docs: samples tag fixes * docs: pg samples pr comments fixes * docs: pg samples checkstyle fixes * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * πŸ¦‰ Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot --- README.md | 5 + .../spanner/AsyncQueryToListAsyncExample.java | 3 + .../CustomTimeoutAndRetrySettingsExample.java | 2 +- .../PgAsyncQueryToListAsyncExample.java | 90 + .../example/spanner/PgAsyncRunnerExample.java | 124 ++ .../PgAsyncTransactionManagerExample.java | 140 ++ .../PgQueryWithNumericParameterSample.java | 60 + .../com/example/spanner/PgSpannerSample.java | 1578 +++++++++++++++++ .../spanner/StatementTimeoutExample.java | 2 +- .../example/spanner/PgAsyncExamplesIT.java | 251 +++ .../example/spanner/PgSpannerSampleIT.java | 304 ++++ .../PgSpannerStandaloneExamplesIT.java | 209 +++ 12 files changed, 2766 insertions(+), 2 deletions(-) create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java diff --git a/README.md b/README.md index 8f78233506a..473c27dff26 100644 --- a/README.md +++ b/README.md @@ -265,10 +265,15 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | Get Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java) | | List Databases Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/ListDatabasesSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/ListDatabasesSample.java) | | List Instance Configs Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java) | +| Pg Async Query To List Async Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java) | +| Pg Async Runner Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java) | +| Pg Async Transaction Manager Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java) | | Pg Batch Dml Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgBatchDmlSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgBatchDmlSample.java) | | Pg Case Sensitivity Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgCaseSensitivitySample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgCaseSensitivitySample.java) | | Pg Interleaved Table Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgInterleavedTableSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgInterleavedTableSample.java) | | Pg Partitioned Dml Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgPartitionedDmlSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgPartitionedDmlSample.java) | +| Pg Query With Numeric Parameter Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java) | +| Pg Spanner Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java) | | Query Information Schema Database Options Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/QueryInformationSchemaDatabaseOptionsSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/QueryInformationSchemaDatabaseOptionsSample.java) | | Query With Json Parameter Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/QueryWithJsonParameterSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/QueryWithJsonParameterSample.java) | | Query With Numeric Parameter Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/QueryWithNumericParameterSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/QueryWithNumericParameterSample.java) | diff --git a/samples/snippets/src/main/java/com/example/spanner/AsyncQueryToListAsyncExample.java b/samples/snippets/src/main/java/com/example/spanner/AsyncQueryToListAsyncExample.java index 57082ba3e05..11da6a13fdd 100644 --- a/samples/snippets/src/main/java/com/example/spanner/AsyncQueryToListAsyncExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/AsyncQueryToListAsyncExample.java @@ -31,6 +31,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +/** + * Example code for using Async query on Cloud Spanner and convert it to list. + */ class AsyncQueryToListAsyncExample { static class Album { final long singerId; diff --git a/samples/snippets/src/main/java/com/example/spanner/CustomTimeoutAndRetrySettingsExample.java b/samples/snippets/src/main/java/com/example/spanner/CustomTimeoutAndRetrySettingsExample.java index e3e51875141..7a4a806fdee 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CustomTimeoutAndRetrySettingsExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CustomTimeoutAndRetrySettingsExample.java @@ -80,7 +80,7 @@ static void executeSqlWithCustomTimeoutAndRetrySettings( .readWriteTransaction() .run(transaction -> { String sql = - "INSERT Singers (SingerId, FirstName, LastName)\n" + "INSERT INTO Singers (SingerId, FirstName, LastName)\n" + "VALUES (20, 'George', 'Washington')"; long rowCount = transaction.executeUpdate(Statement.of(sql)); System.out.printf("%d record inserted.%n", rowCount); diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java new file mode 100644 index 00000000000..63e7205563b --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java @@ -0,0 +1,90 @@ +/* + * Copyright 2022 Google Inc. + * + * 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; + +// [START spanner_postgresql_async_query_to_list] +import com.google.api.core.ApiFuture; +import com.google.cloud.spanner.AsyncResultSet; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class PgAsyncQueryToListAsyncExample { + static class Album { + final long singerId; + final long albumId; + final String albumTitle; + + Album(long singerId, long albumId, String albumTitle) { + this.singerId = singerId; + this.albumId = albumId; + this.albumTitle = albumTitle; + } + } + + static void asyncQueryToList() throws InterruptedException, ExecutionException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + asyncQueryToList(client); + } + } + + // Execute a query asynchronously and transform the result to a list. + static void asyncQueryToList(DatabaseClient client) + throws InterruptedException, ExecutionException, TimeoutException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + ApiFuture> albums; + try (AsyncResultSet resultSet = + client + .singleUse() + .executeQueryAsync(Statement.of("SELECT singerid as \"SingerId\", " + + "albumid as \"AlbumId\", albumtitle as \"AlbumTitle\" " + + "FROM Albums"))) { + // Convert the result set to a list of Albums asynchronously. + albums = + resultSet.toListAsync( + reader -> { + return new Album( + reader.getLong("SingerId"), + reader.getLong("AlbumId"), + reader.getString("AlbumTitle")); + }, + executor); + } + + for (Album album : albums.get(30L, TimeUnit.SECONDS)) { + System.out.printf("%d %d %s%n", album.singerId, album.albumId, album.albumTitle); + } + executor.shutdown(); + } +} +//[END spanner_postgresql_async_query_to_list] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java new file mode 100644 index 00000000000..f05b509d4c8 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java @@ -0,0 +1,124 @@ +/* + * Copyright 2022 Google Inc. + * + * 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; + +//[START spanner_postgresql_async_read_write_transaction] +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.cloud.spanner.AsyncRunner; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.Struct; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +class PgAsyncRunnerExample { + + static void asyncRunner() throws InterruptedException, ExecutionException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + asyncRunner(client); + } + } + + // Execute a read/write transaction asynchronously. + static void asyncRunner(DatabaseClient client) + throws InterruptedException, ExecutionException, TimeoutException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + + // Create an async transaction runner. + AsyncRunner runner = client.runAsync(); + // The transaction returns the total number of rows that were updated as a future array of + // longs. + ApiFuture rowCounts = + runner.runAsync( + txn -> { + // Transfer marketing budget from one album to another. We do it in a + // transaction to ensure that the transfer is atomic. + ApiFuture album1BudgetFut = + txn.readRowAsync("Albums", Key.of(1, 1), ImmutableList.of("MarketingBudget")); + ApiFuture album2BudgetFut = + txn.readRowAsync("Albums", Key.of(2, 2), ImmutableList.of("MarketingBudget")); + + try { + // Transaction will only be committed if this condition still holds at the + // time of commit. Otherwise it will be aborted and the AsyncWork will be + // rerun by the client library. + long transfer = 200_000; + if (album2BudgetFut.get().getLong(0) >= transfer) { + long album1Budget = album1BudgetFut.get().getLong(0); + long album2Budget = album2BudgetFut.get().getLong(0); + + album1Budget += transfer; + album2Budget -= transfer; + Statement updateStatement1 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1 " + + "WHERE SingerId = 1 and AlbumId = 1") + .bind("p1") + .to(album1Budget) + .build(); + Statement updateStatement2 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1 " + + "WHERE SingerId = 2 and AlbumId = 2") + .bind("p1") + .to(album2Budget) + .build(); + return txn.batchUpdateAsync( + ImmutableList.of(updateStatement1, updateStatement2)); + } else { + return ApiFutures.immediateFuture(new long[] {0L, 0L}); + } + } catch (ExecutionException e) { + throw SpannerExceptionFactory.newSpannerException(e.getCause()); + } catch (InterruptedException e) { + throw SpannerExceptionFactory.propagateInterrupt(e); + } + }, + executor); + + ApiFuture totalUpdateCount = + ApiFutures.transform( + rowCounts, + input -> Arrays.stream(input).sum(), + MoreExecutors.directExecutor()); + System.out.printf("%d records updated.%n", totalUpdateCount.get(30L, TimeUnit.SECONDS)); + executor.shutdown(); + } +} +//[END spanner_postgresql_async_read_write_transaction] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java new file mode 100644 index 00000000000..d1b20decde6 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java @@ -0,0 +1,140 @@ +/* + * Copyright 2022 Google Inc. + * + * 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; + +//[START spanner_postgresql_async_transaction_manager] +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.cloud.spanner.AbortedException; +import com.google.cloud.spanner.AsyncTransactionManager; +import com.google.cloud.spanner.AsyncTransactionManager.AsyncTransactionStep; +import com.google.cloud.spanner.AsyncTransactionManager.CommitTimestampFuture; +import com.google.cloud.spanner.AsyncTransactionManager.TransactionContextFuture; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.Struct; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +class PgAsyncTransactionManagerExample { + + static void asyncTransactionManager() + throws InterruptedException, ExecutionException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + asyncTransactionManager(client); + } + } + + static void asyncTransactionManager(DatabaseClient client) + throws InterruptedException, ExecutionException, TimeoutException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + + AsyncTransactionStep, long[]> updateCounts; + try (AsyncTransactionManager mgr = client.transactionManagerAsync()) { + TransactionContextFuture txn = mgr.beginAsync(); + // Loop to retry aborted errors. + while (true) { + try { + updateCounts = + txn.then( + (transaction, v) -> { + // Execute two reads in parallel and return the result of these as the input + // for the next step of the transaction. + ApiFuture album1BudgetFut = + transaction.readRowAsync( + "Albums", Key.of(1, 1), ImmutableList.of("MarketingBudget")); + ApiFuture album2BudgetFut = + transaction.readRowAsync( + "Albums", Key.of(2, 2), ImmutableList.of("MarketingBudget")); + return ApiFutures.allAsList( + Arrays.asList(album1BudgetFut, album2BudgetFut)); + }, + executor) + // The input of the next step of the transaction is the return value of the + // previous step, i.e. a list containing the marketing budget of two Albums. + .then( + (transaction, budgets) -> { + long album1Budget = budgets.get(0).getLong(0); + long album2Budget = budgets.get(1).getLong(0); + long transfer = 200_000; + if (album2Budget >= transfer) { + album1Budget += transfer; + album2Budget -= transfer; + Statement updateStatement1 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1 " + + "WHERE SingerId = 1 and AlbumId = 1") + .bind("p1") + .to(album1Budget) + .build(); + Statement updateStatement2 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1 " + + "WHERE SingerId = 2 and AlbumId = 2") + .bind("p1") + .to(album2Budget) + .build(); + return transaction.batchUpdateAsync( + ImmutableList.of(updateStatement1, updateStatement2)); + } else { + return ApiFutures.immediateFuture(new long[] {0L, 0L}); + } + }, + executor); + // Commit after the updates. + CommitTimestampFuture commitTsFut = updateCounts.commitAsync(); + // Wait for the transaction to finish and execute a retry if necessary. + commitTsFut.get(); + break; + } catch (AbortedException e) { + txn = mgr.resetForRetryAsync(); + } + } + } + + // Calculate the total update count. + ApiFuture totalUpdateCount = + ApiFutures.transform( + updateCounts, + input -> Arrays.stream(input).sum(), + MoreExecutors.directExecutor()); + System.out.printf("%d records updated.%n", totalUpdateCount.get(30L, TimeUnit.SECONDS)); + executor.shutdown(); + } +} +//[END spanner_postgresql_async_transaction_manager] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java b/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java new file mode 100644 index 00000000000..144a26def38 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java @@ -0,0 +1,60 @@ +/* + * Copyright 2022 Google Inc. + * + * 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; + +// [START spanner_postgresql_query_with_numeric_parameter] +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.Value; + +class PgQueryWithNumericParameterSample { + + static void queryWithNumericParameter() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + queryWithNumericParameter(client); + } + } + + static void queryWithNumericParameter(DatabaseClient client) { + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", revenue as \"Revenue\" FROM Venues WHERE Revenue " + + "< $1") + .bind("p1") + .to(Value.pgNumeric("100000")) + .build(); + try (ResultSet resultSet = client.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s%n", resultSet.getLong("VenueId"), resultSet.getValue("Revenue")); + } + } + } +} +// [END spanner_postgresql_query_with_numeric_parameter] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java new file mode 100644 index 00000000000..9cc1d5d8497 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -0,0 +1,1578 @@ +/* + * Copyright 2022 Google Inc. + * + * 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; + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.api.gax.paging.Page; +import com.google.cloud.ByteArray; +import com.google.cloud.Date; +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceId; +import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.KeyRange; +import com.google.cloud.spanner.KeySet; +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; +import com.google.cloud.spanner.ReadOnlyTransaction; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerBatchUpdateException; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.Struct; +import com.google.cloud.spanner.TimestampBound; +import com.google.cloud.spanner.Value; +import com.google.common.io.BaseEncoding; +import com.google.longrunning.Operation; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.spanner.admin.database.v1.CreateBackupMetadata; +import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import com.google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import com.google.spanner.v1.ExecuteSqlRequest; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * Example code for using the Cloud Spanner PostgreSQL interface. + */ +public class PgSpannerSample { + 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")); + 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")); + + /** Class to contain performance sample data. */ + static class Performance { + + final long singerId; + final long venueId; + final String eventDate; + final long revenue; + + Performance(long singerId, long venueId, String eventDate, long revenue) { + this.singerId = singerId; + this.venueId = venueId; + this.eventDate = eventDate; + this.revenue = revenue; + } + } + + // [START spanner_postgresql_insert_data_with_timestamp_column] + static final List PERFORMANCES = + Arrays.asList( + new Performance(1, 4, "2017-10-05", 11000), + new Performance(1, 19, "2017-11-02", 15000), + new Performance(2, 42, "2017-12-23", 7000)); + // [START spanner_postgresql_insert_datatypes_data] + + static Value availableDates1 = + Value.dateArray( + Arrays.asList( + Date.parseDate("2020-12-01"), + Date.parseDate("2020-12-02"), + Date.parseDate("2020-12-03"))); + static Value availableDates2 = + Value.dateArray( + Arrays.asList( + Date.parseDate("2020-11-01"), + Date.parseDate("2020-11-05"), + Date.parseDate("2020-11-15"))); + static Value availableDates3 = + Value.dateArray(Arrays.asList(Date.parseDate("2020-10-01"), Date.parseDate("2020-10-07"))); + // [END spanner_postgresql_insert_data_with_timestamp_column] + static String exampleBytes1 = BaseEncoding.base64().encode("Hello World 1".getBytes()); + static String exampleBytes2 = BaseEncoding.base64().encode("Hello World 2".getBytes()); + static String exampleBytes3 = BaseEncoding.base64().encode("Hello World 3".getBytes()); + static final List VENUES = + Arrays.asList( + new Venue( + 4, + "Venue 4", + exampleBytes1, + 1800, + availableDates1, + "2018-09-02", + false, + 0.85543f, + new BigDecimal("215100.10")), + new Venue( + 19, + "Venue 19", + exampleBytes2, + 6300, + availableDates2, + "2019-01-15", + true, + 0.98716f, + new BigDecimal("1200100.00")), + new Venue( + 42, + "Venue 42", + exampleBytes3, + 3000, + availableDates3, + "2018-10-01", + false, + 0.72598f, + new BigDecimal("390650.99"))); + // [END spanner_postgresql_insert_datatypes_data] + + /** Class to contain venue sample data. */ + static class Venue { + + final long venueId; + final String venueName; + final String venueInfo; + final long capacity; + final Value availableDates; + final String lastContactDate; + final boolean outdoorVenue; + final float popularityScore; + final BigDecimal revenue; + + Venue( + long venueId, + String venueName, + String venueInfo, + long capacity, + Value availableDates, + String lastContactDate, + boolean outdoorVenue, + float popularityScore, + BigDecimal revenue) { + this.venueId = venueId; + this.venueName = venueName; + this.venueInfo = venueInfo; + this.capacity = capacity; + this.availableDates = availableDates; + this.lastContactDate = lastContactDate; + this.outdoorVenue = outdoorVenue; + this.popularityScore = popularityScore; + this.revenue = revenue; + } + } + + // [START spanner_postgresql_create_database] + static void createPostgreSqlDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = dbAdminClient.createDatabase( + dbAdminClient.newDatabaseBuilder(id).setDialect(Dialect.POSTGRESQL).build(), + Collections.emptyList()); + try { + // Initiate the request which returns an OperationFuture. + Database db = op.get(); + System.out.println("Created database [" + db.getId() + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_database] + + // [START spanner_postgresql_insert_data] + static void writeExampleData(DatabaseClient dbClient) { + 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()); + } + dbClient.write(mutations); + } + // [END spanner_postgresql_insert_data] + + // [START spanner_postgresql_delete_data] + static void deleteExampleData(DatabaseClient dbClient) { + List mutations = new ArrayList<>(); + + // KeySet.Builder can be used to delete a specific set of rows. + // Delete the Albums with the key values (2,1) and (2,3). + mutations.add( + Mutation.delete( + "Albums", KeySet.newBuilder().addKey(Key.of(2, 1)).addKey(Key.of(2, 3)).build())); + + // KeyRange can be used to delete rows with a key in a specific range. + // Delete a range of rows where the column key is >=3 and <5 + mutations.add( + Mutation.delete("Singers", KeySet.range(KeyRange.closedOpen(Key.of(3), Key.of(5))))); + + // KeySet.all() can be used to delete all the rows in a table. + // Delete remaining Singers rows, which will also delete the remaining Albums rows since it was + // defined with ON DELETE CASCADE. + mutations.add(Mutation.delete("Singers", KeySet.all())); + + dbClient.write(mutations); + System.out.printf("Records deleted.\n"); + } + // [END spanner_postgresql_delete_data] + + // [START spanner_postgresql_query_data] + static void query(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() // Execute a single read or query against Cloud Spanner. + .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), + resultSet.getString(2)); + } + } + } + // [END spanner_postgresql_query_data] + + // [START spanner_postgresql_read_data] + static void read(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() + .read( + "Albums", + KeySet.all(), // Read all rows in a table. + Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), + resultSet.getString(2)); + } + } + } + // [END spanner_postgresql_read_data] + + // [START spanner_postgresql_add_column] + static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget bigint"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Added MarketingBudget column"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_add_column] + + // Before executing this method, a new column MarketingBudget has to be added to the Albums + // table by applying the DDL statement "ALTER TABLE Albums ADD COLUMN MarketingBudget INT64". + // [START spanner_postgresql_update_data] + static void update(DatabaseClient dbClient) { + // 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(100000) + .build(), + Mutation.newUpdateBuilder("Albums") + .set("SingerId") + .to(2) + .set("AlbumId") + .to(2) + .set("MarketingBudget") + .to(500000) + .build()); + // This writes all the mutations to Cloud Spanner atomically. + dbClient.write(mutations); + } + // [END spanner_postgresql_update_data] + + // [START spanner_postgresql_read_write_transaction] + static void writeWithTransaction(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + // Transfer marketing budget from one album to another. We do it in a transaction to + // ensure that the transfer is atomic. + Struct row = + transaction.readRow("Albums", Key.of(2, 2), Arrays.asList("MarketingBudget")); + long album2Budget = row.getLong(0); + // Transaction will only be committed if this condition still holds at the time of + // commit. Otherwise it will be aborted and the callable will be rerun by the + // client library. + long transfer = 200000; + if (album2Budget >= transfer) { + long album1Budget = + transaction + .readRow("Albums", Key.of(1, 1), Arrays.asList("MarketingBudget")) + .getLong(0); + album1Budget += transfer; + album2Budget -= transfer; + transaction.buffer( + Mutation.newUpdateBuilder("Albums") + .set("SingerId") + .to(1) + .set("AlbumId") + .to(1) + .set("MarketingBudget") + .to(album1Budget) + .build()); + transaction.buffer( + Mutation.newUpdateBuilder("Albums") + .set("SingerId") + .to(2) + .set("AlbumId") + .to(2) + .set("MarketingBudget") + .to(album2Budget) + .build()); + } + return null; + }); + } + // [END spanner_postgresql_read_write_transaction] + + // [START spanner_postgresql_query_data_with_new_column] + static void queryMarketingBudget(DatabaseClient dbClient) { + // Rows without an explicit value for MarketingBudget will have a MarketingBudget equal to + // null. A try-with-resource block is used to automatically release resources held by + // ResultSet. + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery(Statement.of("SELECT singerid as \"SingerId\", " + + "albumid as \"AlbumId\", marketingbudget as \"MarketingBudget\" " + + "FROM Albums"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", + resultSet.getLong("SingerId"), + resultSet.getLong("AlbumId"), + // We check that the value is non null. ResultSet getters can only be used to retrieve + // non null values. + resultSet.isNull("MarketingBudget") ? "NULL" : + resultSet.getLong("MarketingBudget")); + } + } + } + // [END spanner_postgresql_query_data_with_new_column] + + // [START spanner_postgresql_create_index] + static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Added AlbumsByAlbumTitle index"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_index] + + // [START spanner_postgresql_read_data_with_index] + static void readUsingIndex(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() + .readUsingIndex( + "Albums", + "AlbumsByAlbumTitle", + KeySet.all(), + Arrays.asList("AlbumId", "AlbumTitle"))) { + while (resultSet.next()) { + System.out.printf("%d %s\n", resultSet.getLong(0), resultSet.getString(1)); + } + } + } + // [END spanner_postgresql_read_data_with_index] + + // [START spanner_postgresql_create_storing_index] + static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList( + "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) " + + "INCLUDE (MarketingBudget)"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Added AlbumsByAlbumTitle2 index"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_storing_index] + + // Before running this example, create a storing index AlbumsByAlbumTitle2 by applying the DDL + // statement "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) INCLUDE (MarketingBudget)". + // [START spanner_postgresql_read_data_with_storing_index] + static void readStoringIndex(DatabaseClient dbClient) { + // We can read MarketingBudget also from the index since it stores a copy of MarketingBudget. + try (ResultSet resultSet = + dbClient + .singleUse() + .readUsingIndex( + "Albums", + "AlbumsByAlbumTitle2", + KeySet.all(), + Arrays.asList("AlbumId", "AlbumTitle", "MarketingBudget"))) { + while (resultSet.next()) { + System.out.printf( + "%d %s %s\n", + resultSet.getLong(0), + resultSet.getString(1), + resultSet.isNull("marketingbudget") ? "NULL" : resultSet.getLong(2)); + } + } + } + // [END spanner_postgresql_read_data_with_storing_index] + + // [START spanner_postgresql_read_only_transaction] + static void readOnlyTransaction(DatabaseClient dbClient) { + // ReadOnlyTransaction must be closed by calling close() on it to release resources held by it. + // We use a try-with-resource block to automatically do so. + try (ReadOnlyTransaction transaction = dbClient.readOnlyTransaction()) { + ResultSet queryResultSet = + transaction.executeQuery( + Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums")); + while (queryResultSet.next()) { + System.out.printf( + "%d %d %s\n", + queryResultSet.getLong(0), queryResultSet.getLong(1), + queryResultSet.getString(2)); + } + try (ResultSet readResultSet = + transaction.read( + "Albums", KeySet.all(), Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) { + while (readResultSet.next()) { + System.out.printf( + "%d %d %s\n", + readResultSet.getLong(0), readResultSet.getLong(1), + readResultSet.getString(2)); + } + } + } + } + // [END spanner_postgresql_read_only_transaction] + + // [START spanner_postgresql_query_singers_table] + static void querySingersTable(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery(Statement.of("SELECT singerid as \"SingerId\", " + + "firstname as \"FirstName\", lastname as \"LastName\" FROM Singers"))) { + while (resultSet.next()) { + System.out.printf( + "%s %s %s\n", + resultSet.getLong("SingerId"), + resultSet.getString("FirstName"), + resultSet.getString("LastName")); + } + } + } + // [END spanner_postgresql_query_singers_table] + + + // [START spanner_postgresql_dml_getting_started_insert] + static void writeUsingDml(DatabaseClient dbClient) { + // Insert 4 singer records + dbClient + .readWriteTransaction() + .run(transaction -> { + String sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES " + + "(12, 'Melissa', 'Garcia'), " + + "(13, 'Russell', 'Morales'), " + + "(14, 'Jacqueline', 'Long'), " + + "(15, 'Dylan', 'Shaw')"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d records inserted.\n", rowCount); + return null; + }); + } + // [END spanner_postgresql_dml_getting_started_insert] + + // [START spanner_postgresql_query_with_parameter] + static void queryWithParameter(DatabaseClient dbClient) { + Statement statement = + Statement.newBuilder( + "SELECT singerid AS \"SingerId\", " + + "firstname as \"FirstName\", lastname as \"LastName\" " + + "FROM Singers " + + "WHERE LastName = $1") + .bind("p1") + .to("Garcia") + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %s\n", + resultSet.getLong("SingerId"), + resultSet.getString("FirstName"), + resultSet.getString("LastName")); + } + } + } + // [END spanner_postgresql_query_with_parameter] + + // [START spanner_postgresql_dml_getting_started_update] + static void writeWithTransactionUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + // Transfer marketing budget from one album to another. We do it in a transaction to + // ensure that the transfer is atomic. + String sql1 = + "SELECT marketingbudget as \"MarketingBudget\" from Albums WHERE " + + "SingerId = 2 and AlbumId = 2"; + ResultSet resultSet = transaction.executeQuery(Statement.of(sql1)); + long album2Budget = 0; + while (resultSet.next()) { + album2Budget = resultSet.getLong("MarketingBudget"); + } + // Transaction will only be committed if this condition still holds at the time of + // commit. Otherwise it will be aborted and the callable will be rerun by the + // client library. + long transfer = 200000; + if (album2Budget >= transfer) { + String sql2 = + "SELECT marketingbudget as \"MarketingBudget\" from Albums WHERE " + + "SingerId = 1 and AlbumId = 1"; + ResultSet resultSet2 = transaction.executeQuery(Statement.of(sql2)); + long album1Budget = 0; + while (resultSet2.next()) { + album1Budget = resultSet2.getLong("MarketingBudget"); + } + album1Budget += transfer; + album2Budget -= transfer; + Statement updateStatement = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1" + + "WHERE SingerId = 1 and AlbumId = 1") + .bind("p1") + .to(album1Budget) + .build(); + transaction.executeUpdate(updateStatement); + Statement updateStatement2 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1" + + "WHERE SingerId = 2 and AlbumId = 2") + .bind("p1") + .to(album2Budget) + .build(); + transaction.executeUpdate(updateStatement2); + } + return null; + }); + } + // [END spanner_postgresql_dml_getting_started_update] + + // [START spanner_postgresql_create_table_using_ddl] + static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL," + + " FirstName character varying(1024)," + + " LastName character varying(1024)," + + " SingerInfo bytea," + + " PRIMARY KEY (SingerId)" + + ")", + "CREATE TABLE Albums (" + + " SingerId bigint NOT NULL," + + " AlbumId bigint NOT NULL," + + " AlbumTitle character varying(1024)," + + " PRIMARY KEY (SingerId, AlbumId)" + + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Created Singers & Albums tables in database: [" + id + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_table_using_ddl] + + // [START spanner_postgresql_read_stale_data] + static void readStaleData(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse(TimestampBound.ofExactStaleness(15, TimeUnit.SECONDS)) + .read( + "Albums", KeySet.all(), + Arrays.asList("SingerId", "AlbumId", "MarketingBudget"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", + resultSet.getLong(0), + resultSet.getLong(1), + resultSet.isNull(2) ? "NULL" : resultSet.getLong(2)); + } + } + } + // [END spanner_postgresql_read_stale_data] + + // Before executing this method, a new column MarketingBudget has to be added to the Albums + // table by applying the DDL statement "ALTER TABLE Albums ADD COLUMN MarketingBudget BIGINT". + // In addition this update expects the LastUpdateTime column added by applying the DDL statement + // "ALTER TABLE Albums ADD COLUMN LastUpdateTime TIMESTAMPTZ" + // [START spanner_postgresql_update_data_with_timestamp_column] + static void updateWithTimestamp(DatabaseClient dbClient) { + // 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(1000000) + .set("LastUpdateTime") + .to(Value.COMMIT_TIMESTAMP) + .build(), + Mutation.newUpdateBuilder("Albums") + .set("SingerId") + .to(2) + .set("AlbumId") + .to(2) + .set("MarketingBudget") + .to(750000) + .set("LastUpdateTime") + .to(Value.COMMIT_TIMESTAMP) + .build()); + // This writes all the mutations to Cloud Spanner atomically. + dbClient.write(mutations); + } + // [END spanner_postgresql_update_data_with_timestamp_column] + + // [START spanner_postgresql_add_timestamp_column] + static void addLastUpdateTimestampColumn(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList( + "ALTER TABLE Albums ADD COLUMN LastUpdateTime spanner.commit_timestamp"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Added LastUpdateTime as a timestamp column in Albums table."); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_add_timestamp_column] + + // [START spanner_postgresql_query_data_with_timestamp_column] + static void queryMarketingBudgetWithTimestamp(DatabaseClient dbClient) { + // Rows without an explicit value for MarketingBudget will have a MarketingBudget equal to + // null. A try-with-resource block is used to automatically release resources held by + // ResultSet. + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery( + Statement.of( + "SELECT singerid as \"SingerId\", albumid as \"AlbumId\", " + + "marketingbudget as \"MarketingBudget\"," + + "lastupdatetime as \"LastUpdateTime\" FROM Albums" + + " ORDER BY LastUpdateTime DESC"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s %s\n", + resultSet.getLong("SingerId"), + resultSet.getLong("AlbumId"), + // We check that the value is non null. ResultSet getters can only be used to retrieve + // non null values. + resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"), + resultSet.isNull("LastUpdateTime") ? "NULL" : resultSet.getTimestamp("LastUpdateTime")); + } + } + } + // [END spanner_postgresql_query_data_with_timestamp_column] + + // [START spanner_postgresql_create_table_with_timestamp_column] + static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Performances (" + + " SingerId BIGINT NOT NULL," + + " VenueId BIGINT NOT NULL," + + " Revenue BIGINT," + + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," + + " PRIMARY KEY (SingerId, VenueId))" + + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Created Performances table in database: [" + id + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_table_with_timestamp_column] + + // [START spanner_postgresql_insert_data_with_timestamp_column] + static void writeExampleDataWithTimestamp(DatabaseClient dbClient) { + List mutations = new ArrayList<>(); + for (Performance performance : PERFORMANCES) { + mutations.add( + Mutation.newInsertBuilder("Performances") + .set("SingerId") + .to(performance.singerId) + .set("VenueId") + .to(performance.venueId) + .set("Revenue") + .to(performance.revenue) + .set("LastUpdateTime") + .to(Value.COMMIT_TIMESTAMP) + .build()); + } + dbClient.write(mutations); + } + // [END spanner_postgresql_insert_data_with_timestamp_column] + + static void queryPerformancesTable(DatabaseClient dbClient) { + // Rows without an explicit value for Revenue will have a Revenue equal to + // null. A try-with-resource block is used to automatically release resources held by + // ResultSet. + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery( + Statement.of( + "SELECT singerid as \"SingerId\", venueid as \"VenueId\", " + + "revenue as \"Revenue\", lastupdatetime as \"LastUpdateTime\" " + + "FROM Performances ORDER BY LastUpdateTime DESC"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s %s\n", + resultSet.getLong("SingerId"), + resultSet.getLong("VenueId"), + // We check that the value is non null. ResultSet getters can only be used to retrieve + // non null values. + resultSet.isNull("Revenue") ? "NULL" : resultSet.getLong("Revenue"), + resultSet.getTimestamp("LastUpdateTime")); + } + } + } + + // [START spanner_postgresql_dml_standard_insert] + static void insertUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + String sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) " + + " VALUES (10, 'Virginia', 'Watson')"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record inserted.\n", rowCount); + return null; + }); + } + // [END spanner_postgresql_dml_standard_insert] + + // [START spanner_postgresql_dml_standard_update] + static void updateUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + String sql = + "UPDATE Albums " + + "SET MarketingBudget = MarketingBudget * 2 " + + "WHERE SingerId = 1 and AlbumId = 1"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record updated.\n", rowCount); + return null; + }); + } + // [END spanner_postgresql_dml_standard_update] + + // [START spanner_postgresql_dml_standard_delete] + static void deleteUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + String sql = "DELETE FROM Singers WHERE FirstName = 'Alice'"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record deleted.\n", rowCount); + return null; + }); + } + // [END spanner_postgresql_dml_standard_delete] + + // [START spanner_postgresql_dml_write_then_read] + static void writeAndReadUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + // Insert record. + String sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) " + + " VALUES (11, 'Timothy', 'Campbell')"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record inserted.\n", rowCount); + // Read newly inserted record. + sql = "SELECT firstname as \"FirstName\", lastname as \"LastName\" FROM Singers WHERE " + + "SingerId = 11"; + // We use a try-with-resource block to automatically release resources held by + // ResultSet. + try (ResultSet resultSet = transaction.executeQuery(Statement.of(sql))) { + while (resultSet.next()) { + System.out.printf( + "%s %s\n", + resultSet.getString("FirstName"), resultSet.getString("LastName")); + } + } + return null; + }); + } + // [END spanner_postgresql_dml_write_then_read] + + // [START spanner_postgresql_dml_partitioned_update] + static void updateUsingPartitionedDml(DatabaseClient dbClient) { + String sql = "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1"; + long rowCount = dbClient.executePartitionedUpdate(Statement.of(sql)); + System.out.printf("%d records updated.\n", rowCount); + } + // [END spanner_postgresql_dml_partitioned_update] + + // [START spanner_postgresql_dml_partitioned_delete] + static void deleteUsingPartitionedDml(DatabaseClient dbClient) { + String sql = "DELETE FROM Singers WHERE SingerId > 10"; + long rowCount = dbClient.executePartitionedUpdate(Statement.of(sql)); + System.out.printf("%d records deleted.\n", rowCount); + } + // [END spanner_postgresql_dml_partitioned_delete] + + // [START spanner_postgresql_dml_batch_update] + static void updateUsingBatchDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + List stmts = new ArrayList(); + String sql = + "INSERT INTO Albums " + + "(SingerId, AlbumId, AlbumTitle, MarketingBudget) " + + "VALUES (1, 3, 'Test Album Title', 10000) "; + stmts.add(Statement.of(sql)); + sql = + "UPDATE Albums " + + "SET MarketingBudget = MarketingBudget * 2 " + + "WHERE SingerId = 1 and AlbumId = 3"; + stmts.add(Statement.of(sql)); + long[] rowCounts; + try { + rowCounts = transaction.batchUpdate(stmts); + } catch (SpannerBatchUpdateException e) { + rowCounts = e.getUpdateCounts(); + } + for (int i = 0; i < rowCounts.length; i++) { + System.out.printf("%d record updated by stmt %d.\n", rowCounts[i], i); + } + return null; + }); + } + // [END spanner_postgresql_dml_batch_update] + + // [START spanner_postgresql_create_table_with_datatypes] + static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Venues (" + + " VenueId BIGINT NOT NULL," + + " VenueName character varying(100)," + + " VenueInfo bytea," + + " Capacity BIGINT," + + " OutdoorVenue BOOL, " + + " PopularityScore FLOAT8, " + + " Revenue NUMERIC, " + + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," + + " PRIMARY KEY (VenueId))"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Created Venues table in database: [" + id + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_table_with_datatypes] + + // [START spanner_postgresql_insert_datatypes_data] + static void writeDatatypesData(DatabaseClient dbClient) { + List mutations = new ArrayList<>(); + for (Venue venue : VENUES) { + mutations.add( + Mutation.newInsertBuilder("Venues") + .set("VenueId") + .to(venue.venueId) + .set("VenueName") + .to(venue.venueName) + .set("VenueInfo") + .to(venue.venueInfo) + .set("Capacity") + .to(venue.capacity) + .set("OutdoorVenue") + .to(venue.outdoorVenue) + .set("PopularityScore") + .to(venue.popularityScore) + .set("Revenue") + .to(venue.revenue) + .set("LastUpdateTime") + .to(Value.COMMIT_TIMESTAMP) + .build()); + } + dbClient.write(mutations); + } + // [END spanner_postgresql_insert_datatypes_data] + + // [START spanner_postgresql_query_with_bool_parameter] + static void queryWithBool(DatabaseClient dbClient) { + boolean exampleBool = true; + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\"," + + " outdoorvenue as \"OutdoorVenue\" FROM Venues " + + "WHERE OutdoorVenue = $1") + .bind("p1") + .to(exampleBool) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %b\n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getBoolean("OutdoorVenue")); + } + } + } + // [END spanner_postgresql_query_with_bool_parameter] + + // [START spanner_postgresql_query_with_bytes_parameter] + static void queryWithBytes(DatabaseClient dbClient) { + ByteArray exampleBytes = + ByteArray.fromBase64(BaseEncoding.base64().encode("Hello World 1".getBytes())); + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\" FROM Venues " + + "WHERE VenueInfo = $1") + .bind("p1") + .to(exampleBytes) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s\n", resultSet.getLong("VenueId"), resultSet.getString("VenueName")); + } + } + } + // [END spanner_postgresql_query_with_bytes_parameter] + + // [START spanner_postgresql_query_with_float_parameter] + static void queryWithFloat(DatabaseClient dbClient) { + float exampleFloat = 0.8f; + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\", " + + "popularityscore as \"PopularityScore\" FROM Venues " + + "WHERE PopularityScore > $1") + .bind("p1") + .to(exampleFloat) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %f\n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getDouble("PopularityScore")); + } + } + } + // [END spanner_postgresql_query_with_float_parameter] + + // [START spanner_postgresql_query_with_int_parameter] + static void queryWithInt(DatabaseClient dbClient) { + long exampleInt = 3000; + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\", " + + "capacity as \"Capacity\" " + + "FROM Venues " + "WHERE Capacity >= $1") + .bind("p1") + .to(exampleInt) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %d\n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getLong("Capacity")); + } + } + } + // [END spanner_postgresql_query_with_int_parameter] + + // [START spanner_postgresql_query_with_string_parameter] + static void queryWithString(DatabaseClient dbClient) { + String exampleString = "Venue 42"; + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\" FROM Venues WHERE" + + " VenueName = $1") + .bind("p1") + .to(exampleString) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s\n", resultSet.getLong("VenueId"), resultSet.getString("VenueName")); + } + } + } + // [END spanner_postgresql_query_with_string_parameter] + + // [START spanner_postgresql_query_with_timestamp_parameter] + static void queryWithTimestampParameter(DatabaseClient dbClient) { + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\", " + + "lastupdatetime as \"LastUpdateTime\" FROM Venues " + + "WHERE LastUpdateTime < $1") + .bind("p1") + .to(Timestamp.now()) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %s\n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getTimestamp("LastUpdateTime")); + } + } + } + // [END spanner_postgresql_query_with_timestamp_parameter] + + // [START spanner_postgresql_query_with_numeric_parameter] + static void queryWithNumeric(DatabaseClient dbClient) { + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\", " + + "revenue as \"Revenue\" FROM Venues\n" + + "WHERE Revenue >= $1") + .bind("p1") + .to(Value.pgNumeric("300000")) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %s%n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getValue("Revenue")); + } + } + } + // [END spanner_postgresql_query_with_numeric_parameter] + + // [START spanner_postgresql_create_client_with_query_options] + static void clientWithQueryOptions(DatabaseId db) { + SpannerOptions options = + SpannerOptions.newBuilder() + .setDefaultQueryOptions( + db, ExecuteSqlRequest.QueryOptions + .newBuilder() + .setOptimizerVersion("1") + // The list of available statistics packages can be found by querying the + // "INFORMATION_SCHEMA.spanner_postgresql_STATISTICS" table. + .setOptimizerStatisticsPackage("latest") + .build()) + .build(); + Spanner spanner = options.getService(); + DatabaseClient dbClient = spanner.getDatabaseClient(db); + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2)); + } + } + } + // [END spanner_postgresql_create_client_with_query_options] + + // [START spanner_postgresql_query_with_query_options] + static void queryWithQueryOptions(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery( + Statement + .newBuilder("SELECT SingerId, AlbumId, AlbumTitle FROM Albums") + .withQueryOptions(ExecuteSqlRequest.QueryOptions + .newBuilder() + .setOptimizerVersion("1") + // The list of available statistics packages can be found by querying + // the "INFORMATION_SCHEMA.spanner_postgresql_STATISTICS" table. + .setOptimizerStatisticsPackage("latest") + .build()) + .build())) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2)); + } + } + } + // [END spanner_postgresql_query_with_query_options] + + // [START spanner_postgresql_list_backup_operations] + static void listBackupOperations(InstanceAdminClient instanceAdminClient, DatabaseId databaseId) { + Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); + // Get create backup operations for the sample database. + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); + String filter = + String.format( + "(metadata.database:%s) AND " + + "(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.CreateBackupMetadata) AND " + + "(metadata.progress.start_time > \"%s\")", + databaseId.getName(), last24Hours); + Page operations = instance + .listBackupOperations(Options.filter(filter)); + for (com.google.longrunning.Operation op : operations.iterateAll()) { + try { + CreateBackupMetadata metadata = op.getMetadata().unpack(CreateBackupMetadata.class); + System.out.println( + String.format( + "Backup %s on database %s pending: %d%% complete", + metadata.getName(), + metadata.getDatabase(), + metadata.getProgress().getProgressPercent())); + } catch (InvalidProtocolBufferException e) { + // The returned operation does not contain CreateBackupMetadata. + System.err.println(e.getMessage()); + } + } + } + // [END spanner_postgresql_list_backup_operations] + + // [START spanner_postgresql_list_database_operations] + static void listDatabaseOperations( + InstanceAdminClient instanceAdminClient, + DatabaseAdminClient dbAdminClient, + InstanceId instanceId) { + Instance instance = instanceAdminClient.getInstance(instanceId.getInstance()); + // Get optimize restored database operations. + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); + String filter = String.format("(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata) AND " + + "(metadata.progress.start_time > \"%s\")", last24Hours); + for (Operation op : instance.listDatabaseOperations(Options.filter(filter)).iterateAll()) { + try { + OptimizeRestoredDatabaseMetadata metadata = + op.getMetadata().unpack(OptimizeRestoredDatabaseMetadata.class); + System.out.println(String.format( + "Database %s restored from backup is %d%% optimized", + metadata.getName(), + metadata.getProgress().getProgressPercent())); + } catch (InvalidProtocolBufferException e) { + // The returned operation does not contain OptimizeRestoredDatabaseMetadata. + System.err.println(e.getMessage()); + } + } + } + // [END spanner_postgresql_list_database_operations] + + static void run( + DatabaseClient dbClient, + DatabaseAdminClient dbAdminClient, + InstanceAdminClient instanceAdminClient, + String command, + DatabaseId database) { + switch (command) { + case "createpgdatabase": + createPostgreSqlDatabase(dbAdminClient, database); + break; + case "write": + writeExampleData(dbClient); + break; + case "delete": + deleteExampleData(dbClient); + break; + case "query": + query(dbClient); + break; + case "read": + read(dbClient); + break; + case "addmarketingbudget": + addMarketingBudget(dbAdminClient, database); + break; + case "update": + update(dbClient); + break; + case "writetransaction": + writeWithTransaction(dbClient); + break; + case "querymarketingbudget": + queryMarketingBudget(dbClient); + break; + case "addindex": + addIndex(dbAdminClient, database); + break; + case "readindex": + readUsingIndex(dbClient); + break; + case "addstoringindex": + addStoringIndex(dbAdminClient, database); + break; + case "readstoringindex": + readStoringIndex(dbClient); + break; + case "readonlytransaction": + readOnlyTransaction(dbClient); + break; + case "querysingerstable": + querySingersTable(dbClient); + break; + case "writeusingdml": + writeUsingDml(dbClient); + break; + case "querywithparameter": + queryWithParameter(dbClient); + break; + case "writewithtransactionusingdml": + writeWithTransactionUsingDml(dbClient); + break; + case "createtableusingddl": + createTableUsingDdl(dbAdminClient, database); + break; + case "readstaledata": + readStaleData(dbClient); + break; + case "addlastupdatetimestampcolumn": + addLastUpdateTimestampColumn(dbAdminClient, database); + break; + case "updatewithtimestamp": + updateWithTimestamp(dbClient); + break; + case "querywithtimestamp": + queryMarketingBudgetWithTimestamp(dbClient); + break; + case "createtablewithtimestamp": + createTableWithTimestamp(dbAdminClient, database); + break; + case "writewithtimestamp": + writeExampleDataWithTimestamp(dbClient); + break; + case "queryperformancestable": + queryPerformancesTable(dbClient); + break; + case "insertusingdml": + insertUsingDml(dbClient); + break; + case "updateusingdml": + updateUsingDml(dbClient); + break; + case "deleteusingdml": + deleteUsingDml(dbClient); + break; + case "writeandreadusingdml": + writeAndReadUsingDml(dbClient); + break; + case "updateusingpartitioneddml": + updateUsingPartitionedDml(dbClient); + break; + case "deleteusingpartitioneddml": + deleteUsingPartitionedDml(dbClient); + break; + case "updateusingbatchdml": + updateUsingBatchDml(dbClient); + break; + case "createtablewithdatatypes": + createTableWithDatatypes(dbAdminClient, database); + break; + case "writedatatypesdata": + writeDatatypesData(dbClient); + break; + case "querywithbool": + queryWithBool(dbClient); + break; + case "querywithbytes": + queryWithBytes(dbClient); + break; + case "querywithfloat": + queryWithFloat(dbClient); + break; + case "querywithint": + queryWithInt(dbClient); + break; + case "querywithstring": + queryWithString(dbClient); + break; + case "querywithtimestampparameter": + queryWithTimestampParameter(dbClient); + break; + case "querywithnumeric": + queryWithNumeric(dbClient); + break; + case "clientwithqueryoptions": + clientWithQueryOptions(database); + break; + case "querywithqueryoptions": + queryWithQueryOptions(dbClient); + break; + case "listbackupoperations": + listBackupOperations(instanceAdminClient, database); + break; + case "listdatabaseoperations": + listDatabaseOperations(instanceAdminClient, dbAdminClient, database.getInstanceId()); + break; + default: + printUsageAndExit(); + } + } + + static void printUsageAndExit() { + System.err.println("Usage:"); + System.err.println(" PgSpannerExample "); + System.err.println(); + System.err.println("Examples:"); + System.err.println(" PgSpannerExample createpgdatabase my-instance example-db"); + System.err.println(" PgSpannerExample write my-instance example-db"); + System.err.println(" PgSpannerExample delete my-instance example-db"); + System.err.println(" PgSpannerExample query my-instance example-db"); + System.err.println(" PgSpannerExample read my-instance example-db"); + System.err.println(" PgSpannerExample addmarketingbudget my-instance example-db"); + System.err.println(" PgSpannerExample update my-instance example-db"); + System.err.println(" PgSpannerExample writetransaction my-instance example-db"); + System.err.println(" PgSpannerExample querymarketingbudget my-instance example-db"); + System.err.println(" PgSpannerExample addindex my-instance example-db"); + System.err.println(" PgSpannerExample readindex my-instance example-db"); + System.err.println(" PgSpannerExample addstoringindex my-instance example-db"); + System.err.println(" PgSpannerExample readstoringindex my-instance example-db"); + System.err.println(" PgSpannerExample readonlytransaction my-instance example-db"); + System.err.println(" PgSpannerExample querysingerstable my-instance example-db"); + System.err.println(" PgSpannerExample writeusingdml my-instance example-db"); + System.err.println(" PgSpannerExample querywithparameter my-instance example-db"); + System.err.println(" PgSpannerExample writewithtransactionusingdml my-instance example-db"); + System.err.println(" PgSpannerExample createtableforsamples my-instance example-db"); + System.err.println(" PgSpannerExample writewithtimestamp my-instance example-db"); + System.err.println(" PgSpannerExample queryperformancestable my-instance example-db"); + System.err.println(" PgSpannerExample writestructdata my-instance example-db"); + System.err.println(" PgSpannerExample insertusingdml my-instance example-db"); + System.err.println(" PgSpannerExample updateusingdml my-instance example-db"); + System.err.println(" PgSpannerExample deleteusingdml my-instance example-db"); + System.err.println(" PgSpannerExample writeandreadusingdml my-instance example-db"); + System.err.println(" PgSpannerExample writeusingdml my-instance example-db"); + System.err.println(" PgSpannerExample deleteusingpartitioneddml my-instance example-db"); + System.err.println(" PgSpannerExample updateusingbatchdml my-instance example-db"); + System.err.println(" PgSpannerExample createtablewithdatatypes my-instance example-db"); + System.err.println(" PgSpannerExample writedatatypesdata my-instance example-db"); + System.err.println(" PgSpannerExample querywithbool my-instance example-db"); + System.err.println(" PgSpannerExample querywithbytes my-instance example-db"); + System.err.println(" PgSpannerExample querywithfloat my-instance example-db"); + System.err.println(" PgSpannerExample querywithint my-instance example-db"); + System.err.println(" PgSpannerExample querywithstring my-instance example-db"); + System.err.println(" PgSpannerExample querywithtimestampparameter my-instance example-db"); + System.err.println(" PgSpannerExample clientwithqueryoptions my-instance example-db"); + System.err.println(" PgSpannerExample querywithqueryoptions my-instance example-db"); + System.err.println(" PgSpannerExample listbackupoperations my-instance example-db"); + System.err.println(" PgSpannerExample listdatabaseoperations my-instance example-db"); + System.exit(1); + } + + public static void main(String[] args) { + if (args.length != 3) { + printUsageAndExit(); + } + // [START spanner_init_client] + SpannerOptions options = SpannerOptions.newBuilder().build(); + Spanner spanner = options.getService(); + try { + // [END spanner_init_client] + String command = args[0]; + DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]); + + // This will return the default project id based on the environment. + String clientProject = spanner.getOptions().getProjectId(); + if (!db.getInstanceId().getProject().equals(clientProject)) { + System.err.println( + "Invalid project specified. Project in the database id should match the" + + "project name set in the environment variable GOOGLE_CLOUD_PROJECT. Expected: " + + clientProject); + printUsageAndExit(); + } + // [START spanner_init_client] + DatabaseClient dbClient = spanner.getDatabaseClient(db); + DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + // [END spanner_init_client] + + // Use client here... + run(dbClient, dbAdminClient, instanceAdminClient, command, db); + // [START spanner_init_client] + } finally { + spanner.close(); + } + // [END spanner_init_client] + System.out.println("Closed client"); + } + + /** Class to contain singer sample data. */ + static class Singer { + + final long singerId; + final String firstName; + final String lastName; + + Singer(long singerId, String firstName, String lastName) { + this.singerId = singerId; + this.firstName = firstName; + this.lastName = lastName; + } + } + + /** Class to contain album sample data. */ + static class Album { + + final long singerId; + final long albumId; + final String albumTitle; + + Album(long singerId, long albumId, String albumTitle) { + this.singerId = singerId; + this.albumId = albumId; + this.albumTitle = albumTitle; + } + } +} diff --git a/samples/snippets/src/main/java/com/example/spanner/StatementTimeoutExample.java b/samples/snippets/src/main/java/com/example/spanner/StatementTimeoutExample.java index 6c45216b317..2a82467754f 100644 --- a/samples/snippets/src/main/java/com/example/spanner/StatementTimeoutExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/StatementTimeoutExample.java @@ -65,7 +65,7 @@ public ApiCallContext configure(ApiCallContext context, ReqT reque // Run the transaction in the custom context. context.run(() -> client.readWriteTransaction().run(transaction -> { - String sql = "INSERT Singers (SingerId, FirstName, LastName)\n" + String sql = "INSERT INTO Singers (SingerId, FirstName, LastName)\n" + "VALUES (20, 'George', 'Washington')"; long rowCount = transaction.executeUpdate(Statement.of(sql)); System.out.printf("%d record inserted.%n", rowCount); diff --git a/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java b/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java new file mode 100644 index 00000000000..db5c7362cf8 --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java @@ -0,0 +1,251 @@ +/* + * Copyright 2022 Google Inc. + * + * 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; + +import static com.example.spanner.SampleRunner.runSample; +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.KeySet; +import com.google.cloud.spanner.Mutation; +import com.google.common.collect.ImmutableList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Integration tests for Cloud Spanner Async API examples for Postgresql. + */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class PgAsyncExamplesIT extends SampleTestBase { + + private static DatabaseId databaseId; + + @BeforeClass + public static void createTestDatabase() throws Exception { + final String database = idGenerator.generateDatabaseId(); + databaseId = DatabaseId.of(projectId, instanceId, database); + databaseAdminClient + .createDatabase( + databaseAdminClient + .newDatabaseBuilder(databaseId) + .setDialect(Dialect.POSTGRESQL).build(), + Collections.emptyList()) + .get(); + databaseAdminClient.updateDatabaseDdl( + instanceId, + database, + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL," + + " FirstName character varying(1024)," + + " LastName character varying(1024)," + + " SingerInfo bytea," + + " PRIMARY KEY (SingerId)" + + ")", + "CREATE TABLE Albums (" + + " SingerId bigint NOT NULL," + + " AlbumId bigint NOT NULL," + + " AlbumTitle character varying(1024)," + + " MarketingBudget bigint," + + " PRIMARY KEY (SingerId, AlbumId)" + + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE", + "CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), + null).get(); + } + + static class Singer { + + final long singerId; + final String firstName; + final String lastName; + + Singer(long singerId, String firstName, String lastName) { + this.singerId = singerId; + this.firstName = firstName; + this.lastName = lastName; + } + } + + static class Album { + + final long singerId; + final long albumId; + final String albumTitle; + final Long marketingBudget; + + Album(long singerId, long albumId, String albumTitle, Long marketingBudget) { + this.singerId = singerId; + this.albumId = albumId; + this.albumTitle = albumTitle; + this.marketingBudget = marketingBudget; + } + } + + private static final List TEST_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")); + private static final List ALBUMS = + Arrays.asList( + new Album(1, 1, "Total Junk", 300_000L), + new Album(1, 2, "Go, Go, Go", 400_000L), + new Album(2, 1, "Green", 150_000L), + new Album(2, 2, "Forever Hold Your Peace", 350_000L), + new Album(2, 3, "Terrified", null)); + + @Before + public void insertTestData() { + DatabaseClient client = spanner.getDatabaseClient(databaseId); + ImmutableList.Builder mutations = + ImmutableList.builderWithExpectedSize(TEST_SINGERS.size()); + for (Singer singer : TEST_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) + .set("MarketingBudget") + .to(album.marketingBudget) + .build()); + } + client.write(mutations.build()); + } + + private void assertSingersOutput(String out) { + assertThat(out).contains("1 Marc Richard"); + assertThat(out).contains("2 Catalina Smith"); + assertThat(out).contains("3 Alice Trentor"); + assertThat(out).contains("4 Lea Martin"); + assertThat(out).contains("5 David Lomond"); + } + + private void assertAlbumsOutput(String out) { + assertThat(out).contains("1 1 Total Junk"); + assertThat(out).contains("1 2 Go, Go, Go"); + assertThat(out).contains("2 1 Green"); + assertThat(out).contains("2 2 Forever Hold Your Peace"); + assertThat(out).contains("2 3 Terrified"); + } + + @After + public void removeTestData() { + DatabaseClient client = spanner.getDatabaseClient(databaseId); + client.write(Arrays.asList(Mutation.delete("Singers", KeySet.all()))); + } + + @Test + public void asyncQuery_shouldReturnData() throws Exception { + String out = runSample( + () -> AsyncQueryExample.asyncQuery(spanner.getDatabaseClient(databaseId))); + assertAlbumsOutput(out); + } + + @Test + public void asyncQueryToListAsync_shouldReturnData() + throws Exception { + String out = runSample( + () -> PgAsyncQueryToListAsyncExample + .asyncQueryToList(spanner.getDatabaseClient(databaseId))); + assertAlbumsOutput(out); + } + + @Test + public void asyncRead_shouldReturnData() + throws Exception { + String out = runSample(() -> AsyncReadExample.asyncRead(spanner.getDatabaseClient(databaseId))); + assertAlbumsOutput(out); + } + + @Test + public void asyncReadUsingIndex_shouldReturnDataInCorrectOrder() throws Exception { + String out = runSample(() -> AsyncReadUsingIndexExample + .asyncReadUsingIndex(spanner.getDatabaseClient(databaseId))); + assertThat(out) + .contains( + "2 Forever Hold Your Peace\n" + + "2 Go, Go, Go\n" + + "1 Green\n" + + "3 Terrified\n" + + "1 Total Junk"); + } + + @Test + public void asyncReadOnlyTransaction_shouldReturnData() throws Exception { + String out = runSample(() -> AsyncReadOnlyTransactionExample + .asyncReadOnlyTransaction(spanner.getDatabaseClient(databaseId))); + assertAlbumsOutput(out); + assertSingersOutput(out); + } + + @Test + public void asyncDml_shouldInsertRows() throws Exception { + String out = runSample(() -> AsyncDmlExample.asyncDml(spanner.getDatabaseClient(databaseId))); + assertThat(out).contains("4 records inserted."); + } + + @Test + public void asyncRunner_shouldUpdateRows() throws Exception { + String out = runSample( + () -> PgAsyncRunnerExample.asyncRunner(spanner.getDatabaseClient(databaseId))); + assertThat(out).contains("2 records updated."); + } + + @Test + public void asyncTransactionManager_shouldUpdateRows() throws Exception { + String out = runSample(() -> PgAsyncTransactionManagerExample + .asyncTransactionManager(spanner.getDatabaseClient(databaseId))); + assertThat(out).contains("2 records updated."); + } + + @Test + public void asyncReadRow_shouldPrintRow() throws Exception { + String out = runSample( + () -> AsyncReadRowExample.asyncReadRow(spanner.getDatabaseClient(databaseId))); + assertThat(out).contains("1 1 Total Junk"); + assertThat(out).doesNotContain("1 2 Go, Go, Go"); + assertThat(out).doesNotContain("2 1 Green"); + assertThat(out).doesNotContain("2 2 Forever Hold Your Peace"); + assertThat(out).doesNotContain("2 3 Terrified"); + } +} diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java new file mode 100644 index 00000000000..796df092c80 --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java @@ -0,0 +1,304 @@ +/* + * Copyright 2022 Google Inc. + * + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Unit tests for {@code PgSpannerSample} + */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:abbreviationaswordinname") +public class PgSpannerSampleIT { + private static final int DBID_LENGTH = 20; + // The instance needs to exist for tests to pass. + private static final String instanceId = System.getProperty("spanner.test.instance"); + private static final String baseDbId = System.getProperty("spanner.sample.database"); + private static final String databaseId = formatForTest(baseDbId); + private static final String encryptedDatabaseId = formatForTest(baseDbId); + private static final String encryptedBackupId = formatForTest(baseDbId); + private static final String encryptedRestoreId = formatForTest(baseDbId); + static Spanner spanner; + static DatabaseId dbId; + static DatabaseAdminClient dbClient; + + @BeforeClass + public static void setUp() { + SpannerOptions options = + SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build(); + spanner = options.getService(); + dbClient = spanner.getDatabaseAdminClient(); + dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); + // Delete stale test databases that have been created earlier by this test, but not deleted. + deleteStaleTestDatabases(); + } + + static void deleteStaleTestDatabases() { + Timestamp now = Timestamp.now(); + Pattern samplePattern = getTestDbIdPattern(PgSpannerSampleIT.baseDbId); + Pattern restoredPattern = getTestDbIdPattern("restored"); + for (Database db : dbClient.listDatabases(PgSpannerSampleIT.instanceId).iterateAll()) { + if (TimeUnit.HOURS.convert(now.getSeconds() - db.getCreateTime().getSeconds(), + TimeUnit.SECONDS) > 24) { + if (db.getId().getDatabase().length() >= DBID_LENGTH) { + if (samplePattern.matcher(toComparableId(PgSpannerSampleIT.baseDbId, + db.getId().getDatabase())).matches()) { + db.drop(); + } + if (restoredPattern.matcher(toComparableId("restored", db.getId().getDatabase())) + .matches()) { + db.drop(); + } + } + } + } + } + + @AfterClass + public static void tearDown() { + dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); + dbClient.dropDatabase( + dbId.getInstanceId().getInstance(), SpannerSample.createRestoredSampleDbId(dbId)); + dbClient.dropDatabase(instanceId, encryptedDatabaseId); + dbClient.dropDatabase(instanceId, encryptedRestoreId); + dbClient.deleteBackup(instanceId, encryptedBackupId); + spanner.close(); + } + + private static String toComparableId(String baseId, String existingId) { + String zeroUuid = "00000000-0000-0000-0000-0000-00000000"; + int shouldBeLength = (baseId + "-" + zeroUuid).length(); + int missingLength = shouldBeLength - existingId.length(); + return existingId + zeroUuid.substring(zeroUuid.length() - missingLength); + } + + private static Pattern getTestDbIdPattern(String baseDbId) { + return Pattern.compile( + baseDbId + "-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{8}", + Pattern.CASE_INSENSITIVE); + } + + static String formatForTest(String name) { + return name + "-" + UUID.randomUUID().toString().substring(0, DBID_LENGTH); + } + + private String runSample(String command) { + final PrintStream stdOut = System.out; + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + final PrintStream out = new PrintStream(bout); + System.setOut(out); + System.out.println(instanceId + ":" + databaseId); + PgSpannerSample.main(new String[]{command, instanceId, databaseId}); + System.setOut(stdOut); + return bout.toString(); + } + + @Test + public void testSample() throws Exception { + assertThat(instanceId).isNotNull(); + assertThat(databaseId).isNotNull(); + + System.out.println("Create Database ..."); + String out = runSample("createpgdatabase"); + assertThat(out).contains("Created database"); + assertThat(out).contains(dbId.getName()); + + System.out.println("Create sample tables Singers and Albums ..."); + runSample("createtableusingddl"); + + System.out.println("Write data to sample tables ..."); + runSample("write"); + + System.out.println("Read data from sample tables ..."); + out = runSample("read"); + assertThat(out).contains("1 1 Total Junk"); + + System.out.println("Write data using DML to sample table ..."); + runSample("writeusingdml"); + System.out.println("Query Singers table ..."); + out = runSample("querysingerstable"); + assertThat(out).contains("Melissa Garcia"); + out = runSample("query"); + assertThat(out).contains("1 1 Total Junk"); + out = runSample("querywithparameter"); + assertThat(out).contains("12 Melissa Garcia"); + + System.out.println("Add column marketing budget ..."); + runSample("addmarketingbudget"); + + // wait for 15 seconds to elapse and then run an update, and query for stale data + long lastUpdateDataTimeInMillis = System.currentTimeMillis(); + while (System.currentTimeMillis() < lastUpdateDataTimeInMillis + 16000) { + Thread.sleep(1000); + } + System.out.println("Write data to marketing budget ..."); + runSample("update"); + + System.out.println("Query marketing budget ..."); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 100000"); + assertThat(out).contains("2 2 500000"); + + System.out.println("Write with transaction using dml..."); + runSample("writewithtransactionusingdml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 300000"); + assertThat(out).contains("1 1 300000"); + + System.out.println("Add index ..."); + runSample("addindex"); + + System.out.println("Read index ..."); + out = runSample("readindex"); + assertThat(out).contains("Go, Go, Go"); + assertThat(out).contains("Forever Hold Your Peace"); + assertThat(out).contains("Green"); + + System.out.println("Add Storing index ..."); + runSample("addstoringindex"); + + System.out.println("Read storing index ..."); + out = runSample("readstoringindex"); + assertThat(out).contains("300000"); + + System.out.println("Read only transaction ..."); + out = runSample("readonlytransaction"); + assertThat(out.replaceAll("[\r\n]+", " ")) + .containsMatch("(Total Junk.*){2}"); + + System.out.println("Add Timestamp column ..."); + out = runSample("addlastupdatetimestampcolumn"); + assertThat(out).contains("Added LastUpdateTime as a timestamp column"); + + System.out.println("Update values in Timestamp column ..."); + runSample("updatewithtimestamp"); + out = runSample("querywithtimestamp"); + assertThat(out).contains("1 1 1000000"); + assertThat(out).contains("2 2 750000"); + + System.out.println("Create table with Timestamp column ..."); + out = runSample("createtablewithtimestamp"); + assertThat(out).contains("Created Performances table in database"); + + System.out.println("Write with Timestamp ..."); + runSample("writewithtimestamp"); + out = runSample("queryperformancestable"); + assertThat(out).contains("1 4 11000"); + assertThat(out).contains("1 19 15000"); + assertThat(out).contains("2 42 7000"); + + System.out.println("Write using DML ..."); + runSample("insertusingdml"); + out = runSample("querysingerstable"); + assertThat(out).contains("Virginia Watson"); + + System.out.println("Update using DML ..."); + runSample("updateusingdml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 2000000"); + + System.out.println("Delete using DML ..."); + runSample("deleteusingdml"); + out = runSample("querysingerstable"); + assertThat(out).doesNotContain("Alice Trentor"); + + System.out.println("Write and Read using DML ..."); + out = runSample("writeandreadusingdml"); + assertThat(out).contains("Timothy Campbell"); + + System.out.println("Update using partitioned DML ..."); + runSample("updateusingpartitioneddml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("2 2 100000"); + assertThat(out).contains("1 1 2000000"); + + System.out.println("Delete using Partitioned DML ..."); + runSample("deleteusingpartitioneddml"); + out = runSample("querysingerstable"); + assertThat(out).doesNotContain("Timothy Grant"); + assertThat(out).doesNotContain("Melissa Garcia"); + assertThat(out).doesNotContain("Russell Morales"); + assertThat(out).doesNotContain("Jacqueline Long"); + assertThat(out).doesNotContain("Dylan Shaw"); + + System.out.println("Update in Batch using DML ..."); + out = runSample("updateusingbatchdml"); + assertThat(out).contains("1 record updated by stmt 0"); + assertThat(out).contains("1 record updated by stmt 1"); + + System.out.println("Create table with data types ..."); + out = runSample("createtablewithdatatypes"); + assertThat(out).contains("Created Venues table in database"); + + System.out.println("Write into table and Query Boolean Type ..."); + runSample("writedatatypesdata"); + out = runSample("querywithbool"); + assertThat(out).contains("19 Venue 19 true"); + + System.out.println("Query with Bytes ..."); + out = runSample("querywithbytes"); + assertThat(out).contains("4 Venue 4"); + + System.out.println("Query with Float ..."); + out = runSample("querywithfloat"); + assertThat(out).contains("4 Venue 4 0.8"); + assertThat(out).contains("19 Venue 19 0.9"); + + System.out.println("Query with Int ..."); + out = runSample("querywithint"); + assertThat(out).contains("19 Venue 19 6300"); + assertThat(out).contains("42 Venue 42 3000"); + + System.out.println("Query with String ..."); + out = runSample("querywithstring"); + assertThat(out).contains("42 Venue 42"); + + System.out.println("Query with Timestamp parameter ..."); + out = runSample("querywithtimestampparameter"); + assertThat(out).contains("4 Venue 4"); + assertThat(out).contains("19 Venue 19"); + assertThat(out).contains("42 Venue 42"); + + System.out.println("Query with Numeric Type ..."); + out = runSample("querywithnumeric"); + assertThat(out).contains("19 Venue 19 1200100"); + assertThat(out).contains("42 Venue 42 390650.99"); + + System.out.println("Query options ..."); + out = runSample("clientwithqueryoptions"); + assertThat(out).contains("1 1 Total Junk"); + out = runSample("querywithqueryoptions"); + assertThat(out).contains("1 1 Total Junk"); + } +} diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java new file mode 100644 index 00000000000..035b7cdc36b --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java @@ -0,0 +1,209 @@ +/* + * Copyright 2022 Google Inc. + * + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.KeySet; +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.common.collect.ImmutableList; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.concurrent.ExecutionException; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Integration tests for Cloud Spanner cloud client examples. */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class PgSpannerStandaloneExamplesIT { + // The instance needs to exist for tests to pass. + private static String instanceId = System.getProperty("spanner.test.instance"); + private static String baseDatabaseId = System.getProperty("spanner.sample.database", "mysample"); + private static String databaseId = SpannerSampleIT.formatForTest(baseDatabaseId); + private static DatabaseId dbId; + private static DatabaseAdminClient dbClient; + private static Spanner spanner; + + private String runExample(Runnable example) { + PrintStream stdOut = System.out; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(bout); + System.setOut(out); + example.run(); + System.setOut(stdOut); + return bout.toString(); + } + + @BeforeClass + public static void createTestDatabase() throws Exception { + SpannerOptions options = + SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build(); + spanner = options.getService(); + dbClient = spanner.getDatabaseAdminClient(); + if (instanceId == null) { + Iterator iterator = + spanner.getInstanceAdminClient().listInstances().iterateAll().iterator(); + if (iterator.hasNext()) { + instanceId = iterator.next().getId().getInstance(); + } + } + dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); + dbClient + .createDatabase( + dbClient.newDatabaseBuilder(dbId).setDialect(Dialect.POSTGRESQL).build(), + Collections.emptyList()) + .get(); + dbClient.updateDatabaseDdl( + instanceId, + databaseId, + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL," + + " FirstName character varying(1024)," + + " LastName character varying(1024)," + + " SingerInfo bytea," + + " PRIMARY KEY (SingerId)" + + ")", + "CREATE TABLE Venues (" + + "VenueId bigint NOT NULL," + + "Revenue NUMERIC," + + "PRIMARY KEY (VenueId))"), + null).get(); + } + + @AfterClass + public static void dropTestDatabase() throws Exception { + dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); + spanner.close(); + } + + @Before + public void deleteTestData() { + String projectId = spanner.getOptions().getProjectId(); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + client.write(Collections.singleton(Mutation.delete("Singers", KeySet.all()))); + client.write(Collections.singleton(Mutation.delete("Venues", KeySet.all()))); + } + + @Test + public void executeSqlWithCustomTimeoutAndRetrySettings_shouldWriteData() { + String projectId = spanner.getOptions().getProjectId(); + String out = + runExample( + () -> + CustomTimeoutAndRetrySettingsExample.executeSqlWithCustomTimeoutAndRetrySettings( + projectId, instanceId, databaseId)); + assertThat(out).contains("1 record inserted."); + } + + @Test + public void executeSqlWithTimeout_shouldWriteData() { + String projectId = spanner.getOptions().getProjectId(); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + String out = runExample(() -> StatementTimeoutExample.executeSqlWithTimeout(client)); + assertThat(out).contains("1 record inserted."); + } + + @Test + public void addNumericColumn_shouldSuccessfullyAddColumn() + throws InterruptedException, ExecutionException { + OperationFuture operation = + spanner + .getDatabaseAdminClient() + .updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of("ALTER TABLE Venues DROP COLUMN Revenue"), + null); + operation.get(); + String out = + runExample( + () -> { + try { + AddNumericColumnSample.addNumericColumn( + spanner.getDatabaseAdminClient(), instanceId, databaseId); + } catch (ExecutionException e) { + System.out.printf( + "Adding column `Revenue` failed: %s%n", e.getCause().getMessage()); + } catch (InterruptedException e) { + System.out.printf("Adding column `Revenue` was interrupted%n"); + } + }); + assertThat(out).contains("Successfully added column `Revenue`"); + } + + @Test + public void updateNumericData_shouldWriteData() { + String projectId = spanner.getOptions().getProjectId(); + String out = + runExample( + () -> + UpdateNumericDataSample.updateNumericData( + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)))); + assertThat(out).contains("Venues successfully updated"); + } + + @Test + public void queryWithNumericParameter_shouldReturnResults() { + String projectId = spanner.getOptions().getProjectId(); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + client.write( + ImmutableList.of( + Mutation.newInsertOrUpdateBuilder("Venues") + .set("VenueId") + .to(4L) + .set("Revenue") + .to(new BigDecimal("35000")) + .build(), + Mutation.newInsertOrUpdateBuilder("Venues") + .set("VenueId") + .to(19L) + .set("Revenue") + .to(new BigDecimal("104500")) + .build(), + Mutation.newInsertOrUpdateBuilder("Venues") + .set("VenueId") + .to(42L) + .set("Revenue") + .to(new BigDecimal("99999999999999999999999999999.99")) + .build())); + String out = + runExample(() -> PgQueryWithNumericParameterSample.queryWithNumericParameter(client)); + assertThat(out).contains("4 35000"); + } +} From 70838bc8c0401fa38b72b46a9303d426ea6578b1 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Fri, 29 Apr 2022 22:20:35 +0200 Subject: [PATCH 20/24] build(deps): update dependency com.google.cloud:google-cloud-shared-config to v1.4.0 (#1862) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [com.google.cloud:google-cloud-shared-config](https://togithub.com/googleapis/java-shared-config) | `1.3.3` -> `1.4.0` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.4.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.4.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.4.0/compatibility-slim/1.3.3)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.4.0/confidence-slim/1.3.3)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
googleapis/java-shared-config ### [`v1.4.0`](https://togithub.com/googleapis/java-shared-config/blob/HEAD/CHANGELOG.md#​140-httpsgithubcomgoogleapisjava-shared-configcomparev133v140-2022-04-28) [Compare Source](https://togithub.com/googleapis/java-shared-config/compare/v1.3.3...v1.4.0) ##### Features - **java:** remove native image module ([#​471](https://togithub.com/googleapis/java-shared-config/issues/471)) ([7fcba01](https://togithub.com/googleapis/java-shared-config/commit/7fcba016b3138d7beaa4e962853f9bc80f59438c)) ##### [1.3.3](https://togithub.com/googleapis/java-shared-config/compare/v1.3.2...v1.3.3) (2022-04-19) ##### Bug Fixes - **java:** remove protobuf feature from native profile ([#​461](https://togithub.com/googleapis/java-shared-config/issues/461)) ([ffd07cb](https://togithub.com/googleapis/java-shared-config/commit/ffd07cb18ee7d45d4daee1d9ea6f6d321fdca874)) ##### Dependencies - update dependency com.google.cloud:native-image-support to v0.12.11 ([#​459](https://togithub.com/googleapis/java-shared-config/issues/459)) ([d20008d](https://togithub.com/googleapis/java-shared-config/commit/d20008df15209708fdf9d06828b567778190f4d0)) - update dependency com.google.cloud:native-image-support to v0.13.1 ([#​465](https://togithub.com/googleapis/java-shared-config/issues/465)) ([b202064](https://togithub.com/googleapis/java-shared-config/commit/b2020648816feb4740ad70acedfed470d7da5bcf)) ##### [1.3.2](https://togithub.com/googleapis/java-shared-config/compare/v1.3.1...v1.3.2) (2022-03-28) ##### Dependencies - revert google-java-format to 1.7 ([#​453](https://togithub.com/googleapis/java-shared-config/issues/453)) ([cbc777f](https://togithub.com/googleapis/java-shared-config/commit/cbc777f3e9ab75edb6fa2e0268a7446ae4111725)) ##### [1.3.1](https://togithub.com/googleapis/java-shared-config/compare/v1.3.0...v1.3.1) (2022-03-25) ##### Dependencies - update dependency com.google.cloud:native-image-support to v0.12.10 ([#​443](https://togithub.com/googleapis/java-shared-config/issues/443)) ([5b39d5e](https://togithub.com/googleapis/java-shared-config/commit/5b39d5ee15121f052226ff873b6ab101e9c71de5)) - update dependency com.google.googlejavaformat:google-java-format to v1.15.0 ([#​426](https://togithub.com/googleapis/java-shared-config/issues/426)) ([4c3c4b6](https://togithub.com/googleapis/java-shared-config/commit/4c3c4b66129632181e6bc363a0ecccf4f5aac914)) - update dependency org.graalvm.buildtools:junit-platform-native to v0.9.11 ([#​448](https://togithub.com/googleapis/java-shared-config/issues/448)) ([f7f518e](https://togithub.com/googleapis/java-shared-config/commit/f7f518e87d1d9feb9ac54d7c099f97d8751ee3da)) - update dependency org.graalvm.buildtools:native-maven-plugin to v0.9.11 ([#​449](https://togithub.com/googleapis/java-shared-config/issues/449)) ([3e1c0b5](https://togithub.com/googleapis/java-shared-config/commit/3e1c0b5a1d2f4a0db88c06a0d683ed90cbc745e2))
--- ### Configuration πŸ“… **Schedule**: 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, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- google-cloud-spanner-bom/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index d39ec3f6277..2ffb0bca97c 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -8,7 +8,7 @@ com.google.cloud google-cloud-shared-config - 1.3.3 + 1.4.0 Google Cloud Spanner BOM diff --git a/pom.xml b/pom.xml index ed2c8d9c9e0..7ca0dd04fe9 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ com.google.cloud google-cloud-shared-config - 1.3.3 + 1.4.0 From 4f826c592082a8da23cb917a9352ae7b648ba6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Wed, 4 May 2022 11:32:22 +0200 Subject: [PATCH 21/24] test: support PostgreSQL dialect for RandomResultsGenerator (#1868) Adds support for PostgreSQL dialect to RandomResultsGenerator --- .../connection/RandomResultSetGenerator.java | 155 +++++++++++------- 1 file changed, 96 insertions(+), 59 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java index a2870461cd0..024032ab765 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java @@ -16,9 +16,10 @@ package com.google.cloud.spanner.connection; -import com.google.api.client.util.Base64; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Dialect; +import com.google.common.io.BaseEncoding; import com.google.protobuf.ListValue; import com.google.protobuf.NullValue; import com.google.protobuf.Value; @@ -28,6 +29,7 @@ import com.google.spanner.v1.StructType; import com.google.spanner.v1.StructType.Field; import com.google.spanner.v1.Type; +import com.google.spanner.v1.TypeAnnotationCode; import com.google.spanner.v1.TypeCode; import java.math.BigDecimal; import java.util.Random; @@ -37,86 +39,109 @@ * of Cloud Spanner filled with random data. */ public class RandomResultSetGenerator { - private static final Type[] TYPES = - new Type[] { - Type.newBuilder().setCode(TypeCode.BOOL).build(), - Type.newBuilder().setCode(TypeCode.INT64).build(), - Type.newBuilder().setCode(TypeCode.FLOAT64).build(), - Type.newBuilder().setCode(TypeCode.NUMERIC).build(), - Type.newBuilder().setCode(TypeCode.STRING).build(), - Type.newBuilder().setCode(TypeCode.JSON).build(), - Type.newBuilder().setCode(TypeCode.BYTES).build(), - Type.newBuilder().setCode(TypeCode.DATE).build(), - Type.newBuilder().setCode(TypeCode.TIMESTAMP).build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.BOOL)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.INT64)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.FLOAT64)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.NUMERIC)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.STRING)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.JSON)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.BYTES)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.DATE)) - .build(), - Type.newBuilder() - .setCode(TypeCode.ARRAY) - .setArrayElementType(Type.newBuilder().setCode(TypeCode.TIMESTAMP)) - .build(), - }; + private static Type[] generateTypes(Dialect dialect) { + return new Type[] { + Type.newBuilder().setCode(TypeCode.BOOL).build(), + Type.newBuilder().setCode(TypeCode.INT64).build(), + Type.newBuilder().setCode(TypeCode.FLOAT64).build(), + dialect == Dialect.POSTGRESQL + ? Type.newBuilder() + .setCode(TypeCode.NUMERIC) + .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) + .build() + : Type.newBuilder().setCode(TypeCode.NUMERIC).build(), + Type.newBuilder().setCode(TypeCode.STRING).build(), + Type.newBuilder() + .setCode(dialect == Dialect.POSTGRESQL ? TypeCode.STRING : TypeCode.JSON) + .build(), + Type.newBuilder().setCode(TypeCode.BYTES).build(), + Type.newBuilder().setCode(TypeCode.DATE).build(), + Type.newBuilder().setCode(TypeCode.TIMESTAMP).build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.BOOL)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.INT64)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.FLOAT64)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + dialect == Dialect.POSTGRESQL + ? Type.newBuilder() + .setCode(TypeCode.NUMERIC) + .setTypeAnnotation(TypeAnnotationCode.PG_NUMERIC) + : Type.newBuilder().setCode(TypeCode.NUMERIC)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.STRING)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType( + Type.newBuilder() + .setCode(dialect == Dialect.POSTGRESQL ? TypeCode.STRING : TypeCode.JSON)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.BYTES)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.DATE)) + .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.TIMESTAMP)) + .build(), + }; + } - private static ResultSetMetadata generateMetadata() { + private static ResultSetMetadata generateMetadata(Type[] types) { StructType.Builder rowTypeBuilder = StructType.newBuilder(); - for (int col = 0; col < TYPES.length; col++) { - rowTypeBuilder.addFields(Field.newBuilder().setName("COL" + col).setType(TYPES[col])).build(); + for (int col = 0; col < types.length; col++) { + rowTypeBuilder.addFields(Field.newBuilder().setName("COL" + col).setType(types[col])).build(); } ResultSetMetadata.Builder builder = ResultSetMetadata.newBuilder(); builder.setRowType(rowTypeBuilder.build()); return builder.build(); } - private static final ResultSetMetadata METADATA = generateMetadata(); - + private final ResultSetMetadata metadata; + private final Dialect dialect; + private final Type[] types; private final int rowCount; private final Random random = new Random(); public RandomResultSetGenerator(int rowCount) { + this(rowCount, Dialect.GOOGLE_STANDARD_SQL); + } + + public RandomResultSetGenerator(int rowCount, Dialect dialect) { this.rowCount = rowCount; + this.dialect = dialect; + this.types = generateTypes(dialect); + this.metadata = generateMetadata(types); } public ResultSet generate() { ResultSet.Builder builder = ResultSet.newBuilder(); for (int row = 0; row < rowCount; row++) { ListValue.Builder rowBuilder = ListValue.newBuilder(); - for (Type type : TYPES) { + for (Type type : types) { Value.Builder valueBuilder = Value.newBuilder(); setRandomValue(valueBuilder, type); rowBuilder.addValues(valueBuilder.build()); } builder.addRows(rowBuilder.build()); } - builder.setMetadata(METADATA); + builder.setMetadata(metadata); return builder.build(); } @@ -142,7 +167,7 @@ private void setRandomValue(Value.Builder builder, Type type) { case BYTES: byte[] bytes = new byte[random.nextInt(200)]; random.nextBytes(bytes); - builder.setStringValue(Base64.encodeBase64String(bytes)); + builder.setStringValue(BaseEncoding.base64().encode(bytes)); break; case JSON: builder.setStringValue("\"" + random.nextInt(200) + "\":\"" + random.nextInt(200) + "\""); @@ -154,10 +179,18 @@ private void setRandomValue(Value.Builder builder, Type type) { builder.setStringValue(date.toString()); break; case FLOAT64: - builder.setNumberValue(random.nextDouble()); + if (randomNaN()) { + builder.setNumberValue(Double.NaN); + } else { + builder.setNumberValue(random.nextDouble()); + } break; case NUMERIC: - builder.setStringValue(BigDecimal.valueOf(random.nextDouble()).toString()); + if (dialect == Dialect.POSTGRESQL && randomNaN()) { + builder.setStringValue("NaN"); + } else { + builder.setStringValue(BigDecimal.valueOf(random.nextDouble()).toString()); + } break; case INT64: builder.setStringValue(String.valueOf(random.nextLong())); @@ -184,4 +217,8 @@ private void setRandomValue(Value.Builder builder, Type type) { private boolean randomNull() { return random.nextInt(10) == 0; } + + private boolean randomNaN() { + return random.nextInt(10) == 0; + } } From f74c77d3786d37173914dbddbaca12ba1209adc5 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 5 May 2022 02:02:11 +0200 Subject: [PATCH 22/24] build(deps): update dependency org.apache.maven.plugins:maven-project-info-reports-plugin to v3.3.0 (#1859) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [org.apache.maven.plugins:maven-project-info-reports-plugin](https://maven.apache.org/plugins/) | `3.2.2` -> `3.3.0` | [![age](https://badges.renovateapi.com/packages/maven/org.apache.maven.plugins:maven-project-info-reports-plugin/3.3.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/org.apache.maven.plugins:maven-project-info-reports-plugin/3.3.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/org.apache.maven.plugins:maven-project-info-reports-plugin/3.3.0/compatibility-slim/3.2.2)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/org.apache.maven.plugins:maven-project-info-reports-plugin/3.3.0/confidence-slim/3.2.2)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration πŸ“… **Schedule**: 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, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-spanner). --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7ca0dd04fe9..6f81a5acc31 100644 --- a/pom.xml +++ b/pom.xml @@ -159,7 +159,7 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 3.2.2 + 3.3.0 From f1d2d3ef1dbd30c153616c2efcc362c1330705e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 5 May 2022 06:31:50 +0200 Subject: [PATCH 23/24] feat: support CredentialsProvider in Connection API (#1869) * feat: support CredentialsProvider in Connection API Adds suppport for setting a CredentialsProvider instead of a credentialsUrl in a connection string. The CredentialsProvider reference must be a class name to a public class with a public no-arg constructor. This option is available in the Connection API, which means that any client that uses that API can directly benefit from it (this effectively means the JDBC driver). Fixes b/231174409 --- .../spanner/connection/ConnectionImpl.java | 5 + .../spanner/connection/ConnectionOptions.java | 97 +++++++++-- .../cloud/spanner/connection/SpannerPool.java | 28 +-- .../connection/AbstractMockServerTest.java | 2 +- .../connection/ConnectionOptionsTest.java | 162 +++++++++++------- .../connection/CredentialsProviderTest.java | 126 ++++++++++++++ 6 files changed, 332 insertions(+), 88 deletions(-) create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/CredentialsProviderTest.java diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java index 1a7d9b76682..6770cf42d90 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java @@ -259,6 +259,11 @@ static UnitOfWorkType of(TransactionMode transactionMode) { setDefaultTransactionOptions(); } + @VisibleForTesting + Spanner getSpanner() { + return this.spanner; + } + private DdlClient createDdlClient() { return DdlClient.newBuilder() .setDatabaseAdminClient(spanner.getDatabaseAdminClient()) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java index 0dba8931573..7923156da1c 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java @@ -17,6 +17,7 @@ package com.google.cloud.spanner.connection; import com.google.api.core.InternalApi; +import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.rpc.TransportChannelProvider; import com.google.auth.Credentials; import com.google.auth.oauth2.AccessToken; @@ -36,15 +37,20 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; import javax.annotation.Nullable; /** @@ -182,6 +188,8 @@ public String[] getValidValues() { public static final String CREDENTIALS_PROPERTY_NAME = "credentials"; /** Name of the 'encodedCredentials' connection property. */ public static final String ENCODED_CREDENTIALS_PROPERTY_NAME = "encodedCredentials"; + /** Name of the 'credentialsProvider' connection property. */ + public static final String CREDENTIALS_PROVIDER_PROPERTY_NAME = "credentialsProvider"; /** * OAuth token to use for authentication. Cannot be used in combination with a credentials file. */ @@ -231,6 +239,9 @@ public String[] getValidValues() { ConnectionProperty.createStringProperty( ENCODED_CREDENTIALS_PROPERTY_NAME, "Base64-encoded credentials to use for this connection. If neither this property or a credentials location are set, the connection will use the default Google Cloud credentials for the runtime environment."), + ConnectionProperty.createStringProperty( + CREDENTIALS_PROVIDER_PROPERTY_NAME, + "The class name of the com.google.api.gax.core.CredentialsProvider implementation that should be used to obtain credentials for connections."), ConnectionProperty.createStringProperty( OAUTH_TOKEN_PROPERTY_NAME, "A valid pre-existing OAuth token to use for authentication for this connection. Setting this property will take precedence over any value set for a credentials file."), @@ -386,6 +397,12 @@ private boolean isValidUri(String uri) { *
  • encodedCredentials (String): A Base64 encoded string containing the Google credentials * to use. You should only set either this property or the `credentials` (file location) * property, but not both at the same time. + *
  • credentialsProvider (String): Class name of the {@link + * com.google.api.gax.core.CredentialsProvider} that should be used to get credentials for + * a connection that is created by this {@link ConnectionOptions}. The credentials will be + * retrieved from the {@link com.google.api.gax.core.CredentialsProvider} when a new + * connection is created. A connection will use the credentials that were obtained at + * creation during its lifetime. *
  • autocommit (boolean): Sets the initial autocommit mode for the connection. Default is * true. *
  • readonly (boolean): Sets the initial readonly mode for the connection. Default is @@ -501,6 +518,7 @@ public static Builder newBuilder() { private final String warnings; private final String credentialsUrl; private final String encodedCredentials; + private final CredentialsProvider credentialsProvider; private final String oauthToken; private final Credentials fixedCredentials; @@ -537,22 +555,22 @@ private ConnectionOptions(Builder builder) { this.credentialsUrl = builder.credentialsUrl != null ? builder.credentialsUrl : parseCredentials(builder.uri); this.encodedCredentials = parseEncodedCredentials(builder.uri); - // Check that not both a credentials location and encoded credentials have been specified in the - // connection URI. - Preconditions.checkArgument( - this.credentialsUrl == null || this.encodedCredentials == null, - "Cannot specify both a credentials URL and encoded credentials. Only set one of the properties."); - + this.credentialsProvider = parseCredentialsProvider(builder.uri); this.oauthToken = builder.oauthToken != null ? builder.oauthToken : parseOAuthToken(builder.uri); - this.fixedCredentials = builder.credentials; - // Check that not both credentials and an OAuth token have been specified. + // Check that at most one of credentials location, encoded credentials, credentials provider and + // OUAuth token has been specified in the connection URI. Preconditions.checkArgument( - (builder.credentials == null - && this.credentialsUrl == null - && this.encodedCredentials == null) - || this.oauthToken == null, - "Cannot specify both credentials and an OAuth token."); + Stream.of( + this.credentialsUrl, + this.encodedCredentials, + this.credentialsProvider, + this.oauthToken) + .filter(Objects::nonNull) + .count() + <= 1, + "Specify only one of credentialsUrl, encodedCredentials, credentialsProvider and OAuth token"); + this.fixedCredentials = builder.credentials; this.userAgent = parseUserAgent(this.uri); QueryOptions.Builder queryOptionsBuilder = QueryOptions.newBuilder(); @@ -570,14 +588,24 @@ private ConnectionOptions(Builder builder) { // Using credentials on a plain text connection is not allowed, so if the user has not specified // any credentials and is using a plain text connection, we should not try to get the // credentials from the environment, but default to NoCredentials. - if (builder.credentials == null + if (this.fixedCredentials == null && this.credentialsUrl == null && this.encodedCredentials == null + && this.credentialsProvider == null && this.oauthToken == null && this.usePlainText) { this.credentials = NoCredentials.getInstance(); } else if (this.oauthToken != null) { this.credentials = new GoogleCredentials(new AccessToken(oauthToken, null)); + } else if (this.credentialsProvider != null) { + try { + this.credentials = this.credentialsProvider.getCredentials(); + } catch (IOException exception) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + "Failed to get credentials from CredentialsProvider: " + exception.getMessage(), + exception); + } } else if (this.fixedCredentials != null) { this.credentials = fixedCredentials; } else if (this.encodedCredentials != null) { @@ -691,18 +719,49 @@ static boolean parseRetryAbortsInternally(String uri) { } @VisibleForTesting - static String parseCredentials(String uri) { + static @Nullable String parseCredentials(String uri) { String value = parseUriProperty(uri, CREDENTIALS_PROPERTY_NAME); return value != null ? value : DEFAULT_CREDENTIALS; } @VisibleForTesting - static String parseEncodedCredentials(String uri) { + static @Nullable String parseEncodedCredentials(String uri) { return parseUriProperty(uri, ENCODED_CREDENTIALS_PROPERTY_NAME); } @VisibleForTesting - static String parseOAuthToken(String uri) { + static @Nullable CredentialsProvider parseCredentialsProvider(String uri) { + String name = parseUriProperty(uri, CREDENTIALS_PROVIDER_PROPERTY_NAME); + if (name != null) { + try { + Class clazz = + (Class) Class.forName(name); + Constructor constructor = clazz.getDeclaredConstructor(); + return constructor.newInstance(); + } catch (ClassNotFoundException classNotFoundException) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + "Unknown or invalid CredentialsProvider class name: " + name, + classNotFoundException); + } catch (NoSuchMethodException noSuchMethodException) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + "Credentials provider " + name + " does not have a public no-arg constructor.", + noSuchMethodException); + } catch (InvocationTargetException + | InstantiationException + | IllegalAccessException exception) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + "Failed to create an instance of " + name + ": " + exception.getMessage(), + exception); + } + } + return null; + } + + @VisibleForTesting + static @Nullable String parseOAuthToken(String uri) { String value = parseUriProperty(uri, OAUTH_TOKEN_PROPERTY_NAME); return value != null ? value : DEFAULT_OAUTH_TOKEN; } @@ -849,6 +908,10 @@ Credentials getFixedCredentials() { return this.fixedCredentials; } + CredentialsProvider getCredentialsProvider() { + return this.credentialsProvider; + } + /** The {@link SessionPoolOptions} of this {@link ConnectionOptions}. */ public SessionPoolOptions getSessionPoolOptions() { return sessionPoolOptions; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java index 2712143da7a..f94e27d963d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java @@ -27,12 +27,10 @@ import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; -import com.google.common.base.Predicates; import com.google.common.base.Ticker; -import com.google.common.collect.Iterables; import io.grpc.ManagedChannelBuilder; +import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -43,6 +41,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Stream; import javax.annotation.concurrent.GuardedBy; /** @@ -120,15 +119,17 @@ static class CredentialsKey { static final Object DEFAULT_CREDENTIALS_KEY = new Object(); final Object key; - static CredentialsKey create(ConnectionOptions options) { + static CredentialsKey create(ConnectionOptions options) throws IOException { return new CredentialsKey( - Iterables.find( - Arrays.asList( + Stream.of( options.getOAuthToken(), + options.getCredentialsProvider() == null ? null : options.getCredentials(), options.getFixedCredentials(), options.getCredentialsUrl(), - DEFAULT_CREDENTIALS_KEY), - Predicates.notNull())); + DEFAULT_CREDENTIALS_KEY) + .filter(Objects::nonNull) + .findFirst() + .get()); } private CredentialsKey(Object key) { @@ -155,10 +156,17 @@ static class SpannerPoolKey { @VisibleForTesting static SpannerPoolKey of(ConnectionOptions options) { - return new SpannerPoolKey(options); + try { + return new SpannerPoolKey(options); + } catch (IOException ioException) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.INVALID_ARGUMENT, + "Failed to get credentials: " + ioException.getMessage(), + ioException); + } } - private SpannerPoolKey(ConnectionOptions options) { + private SpannerPoolKey(ConnectionOptions options) throws IOException { this.host = options.getHost(); this.projectId = options.getProjectId(); this.credentialsKey = CredentialsKey.create(options); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java index 6cf838845b5..7f9163e8e46 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java @@ -173,7 +173,7 @@ public static void stopServer() { try { SpannerPool.INSTANCE.checkAndCloseSpanners( CheckAndCloseSpannersMode.ERROR, - new ForceCloseSpannerFunction(100L, TimeUnit.MILLISECONDS)); + new ForceCloseSpannerFunction(500L, TimeUnit.MILLISECONDS)); } finally { Logger.getLogger(AbstractFuture.class.getName()).setUseParentHandlers(futureParentHandlers); Logger.getLogger(LogExceptionRunnable.class.getName()) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java index 837783ee18b..cb862ca3061 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java @@ -20,10 +20,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.auth.Credentials; import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.cloud.NoCredentials; @@ -33,6 +35,7 @@ import com.google.common.io.BaseEncoding; import com.google.common.io.Files; import java.io.File; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.Collections; @@ -254,22 +257,14 @@ public void testBuilderSetUri() { } private void setInvalidUri(ConnectionOptions.Builder builder, String uri) { - try { - builder.setUri(uri); - fail(uri + " should be considered an invalid uri"); - } catch (IllegalArgumentException e) { - // Expected exception - } + assertThrows(IllegalArgumentException.class, () -> builder.setUri(uri)); } private void setInvalidProperty( ConnectionOptions.Builder builder, String uri, String expectedInvalidProperties) { - try { - builder.setUri(uri); - fail("missing expected exception"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(expectedInvalidProperties); - } + IllegalArgumentException exception = + assertThrows(IllegalArgumentException.class, () -> builder.setUri(uri)); + assertTrue(exception.getMessage(), exception.getMessage().contains(expectedInvalidProperties)); } @Test @@ -381,12 +376,14 @@ public void testParseOAuthToken() { .setUri( "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database" + "?OAuthToken=RsT5OjbzRn430zqMLgV3Ia;credentials=/path/to/credentials.json"); - try { - builder.build(); - fail("missing expected exception"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains("Cannot specify both credentials and an OAuth token"); - } + IllegalArgumentException exception = + assertThrows(IllegalArgumentException.class, builder::build); + assertTrue( + exception.getMessage(), + exception + .getMessage() + .contains( + "Specify only one of credentialsUrl, encodedCredentials, credentialsProvider and OAuth token")); // Now try to use only an OAuth token. builder = @@ -416,17 +413,22 @@ public void testSetOAuthToken() { @Test public void testSetOAuthTokenAndCredentials() { - try { - ConnectionOptions.newBuilder() - .setUri( - "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database") - .setOAuthToken("RsT5OjbzRn430zqMLgV3Ia") - .setCredentialsUrl(FILE_TEST_PATH) - .build(); - fail("missing expected exception"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains("Cannot specify both credentials and an OAuth token"); - } + IllegalArgumentException exception = + assertThrows( + IllegalArgumentException.class, + () -> + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database") + .setOAuthToken("RsT5OjbzRn430zqMLgV3Ia") + .setCredentialsUrl(FILE_TEST_PATH) + .build()); + assertTrue( + exception.getMessage(), + exception + .getMessage() + .contains( + "Specify only one of credentialsUrl, encodedCredentials, credentialsProvider and OAuth token")); } @Test @@ -451,16 +453,16 @@ public void testLenient() { assertThat(options.getWarnings()).contains("bar"); assertThat(options.getWarnings()).doesNotContain("lenient"); - try { - ConnectionOptions.newBuilder() - .setUri( - "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database?bar=foo") - .setCredentialsUrl(FILE_TEST_PATH) - .build(); - fail("missing expected exception"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains("bar"); - } + IllegalArgumentException exception = + assertThrows( + IllegalArgumentException.class, + () -> + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database?bar=foo") + .setCredentialsUrl(FILE_TEST_PATH) + .build()); + assertTrue(exception.getMessage(), exception.getMessage().contains("bar")); } @Test @@ -492,29 +494,31 @@ public void testLocalConnectionError() { String uri = "cloudspanner://localhost:1/projects/test-project/instances/test-instance/databases/test-database?usePlainText=true"; ConnectionOptions options = ConnectionOptions.newBuilder().setUri(uri).build(); - try (Connection connection = options.getConnection()) { - fail("Missing expected exception"); - } catch (SpannerException e) { - assertEquals(ErrorCode.UNAVAILABLE, e.getErrorCode()); - assertThat(e.getMessage()) - .contains( - String.format( - "The connection string '%s' contains host 'localhost:1', but no running", uri)); - } + SpannerException exception = assertThrows(SpannerException.class, options::getConnection); + assertEquals(ErrorCode.UNAVAILABLE, exception.getErrorCode()); + assertTrue( + exception.getMessage(), + exception + .getMessage() + .contains( + String.format( + "The connection string '%s' contains host 'localhost:1', but no running", + uri))); } @Test public void testInvalidCredentials() { String uri = "cloudspanner:/projects/test-project/instances/test-instance/databases/test-database?credentials=/some/non/existing/path"; - try { - ConnectionOptions.newBuilder().setUri(uri).build(); - fail("Missing expected exception"); - } catch (SpannerException e) { - assertEquals(ErrorCode.INVALID_ARGUMENT, e.getErrorCode()); - assertThat(e.getMessage()) - .contains("Invalid credentials path specified: /some/non/existing/path"); - } + SpannerException exception = + assertThrows( + SpannerException.class, () -> ConnectionOptions.newBuilder().setUri(uri).build()); + assertEquals(ErrorCode.INVALID_ARGUMENT, exception.getErrorCode()); + assertTrue( + exception.getMessage(), + exception + .getMessage() + .contains("Invalid credentials path specified: /some/non/existing/path")); } @Test @@ -571,9 +575,47 @@ public void testSetCredentialsAndEncodedCredentials() throws Exception { assertThrows( IllegalArgumentException.class, () -> ConnectionOptions.newBuilder().setUri(uri).build()); - assertThat(e.getMessage()) - .contains( - "Cannot specify both a credentials URL and encoded credentials. Only set one of the properties."); + assertTrue( + e.getMessage(), + e.getMessage() + .contains( + "Specify only one of credentialsUrl, encodedCredentials, credentialsProvider and OAuth token")); + } + + public static class TestCredentialsProvider implements CredentialsProvider { + @Override + public Credentials getCredentials() throws IOException { + return NoCredentials.getInstance(); + } + } + + @Test + public void testValidCredentialsProvider() { + String uri = + String.format( + "cloudspanner:/projects/test-project/instances/test-instance/databases/test-database?credentialsProvider=%s", + TestCredentialsProvider.class.getName()); + + ConnectionOptions options = ConnectionOptions.newBuilder().setUri(uri).build(); + assertEquals(NoCredentials.getInstance(), options.getCredentials()); + } + + @Test + public void testSetCredentialsAndCredentialsProvider() { + String uri = + String.format( + "cloudspanner:/projects/test-project/instances/test-instance/databases/test-database?credentials=%s;credentialsProvider=%s", + FILE_TEST_PATH, NoCredentialsProvider.class.getName()); + + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> ConnectionOptions.newBuilder().setUri(uri).build()); + assertTrue( + e.getMessage(), + e.getMessage() + .contains( + "Specify only one of credentialsUrl, encodedCredentials, credentialsProvider and OAuth token")); } @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/CredentialsProviderTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/CredentialsProviderTest.java new file mode 100644 index 00000000000..da7751f3889 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/CredentialsProviderTest.java @@ -0,0 +1,126 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.connection; + +import static org.junit.Assert.assertEquals; + +import com.google.api.gax.core.CredentialsProvider; +import com.google.auth.Credentials; +import com.google.auth.oauth2.OAuth2Credentials; +import io.grpc.ManagedChannelBuilder; +import java.io.IOException; +import java.io.ObjectStreamException; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class CredentialsProviderTest extends AbstractMockServerTest { + private static final AtomicInteger COUNTER = new AtomicInteger(); + + @BeforeClass + public static void resetCounter() { + COUNTER.set(0); + } + + private static final class TestCredentials extends OAuth2Credentials { + private final int id; + + private TestCredentials(int id) { + this.id = id; + } + + private Object readResolve() throws ObjectStreamException { + return this; + } + + public boolean equals(Object obj) { + if (!(obj instanceof TestCredentials)) { + return false; + } + return this.id == ((TestCredentials) obj).id; + } + + public int hashCode() { + return System.identityHashCode(this.id); + } + } + + static final class TestCredentialsProvider implements CredentialsProvider { + @Override + public Credentials getCredentials() throws IOException { + return new TestCredentials(COUNTER.incrementAndGet()); + } + } + + @Test + public void testCredentialsProvider() { + ConnectionOptions options = + ConnectionOptions.newBuilder() + .setUri( + String.format( + "cloudspanner://localhost:%d/projects/proj/instances/inst/databases/db?credentialsProvider=%s", + getPort(), TestCredentialsProvider.class.getName())) + .setConfigurator( + spannerOptions -> + spannerOptions.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)) + .build(); + + try (Connection connection = options.getConnection()) { + assertEquals( + TestCredentials.class, + ((ConnectionImpl) connection).getSpanner().getOptions().getCredentials().getClass()); + TestCredentials credentials = + (TestCredentials) + ((ConnectionImpl) connection).getSpanner().getOptions().getCredentials(); + assertEquals(1, credentials.id); + } + // The second connection should get the same credentials from the provider. + try (Connection connection = options.getConnection()) { + assertEquals( + TestCredentials.class, + ((ConnectionImpl) connection).getSpanner().getOptions().getCredentials().getClass()); + TestCredentials credentials = + (TestCredentials) + ((ConnectionImpl) connection).getSpanner().getOptions().getCredentials(); + assertEquals(1, credentials.id); + } + + // Creating new ConnectionOptions should refresh the credentials. + options = + ConnectionOptions.newBuilder() + .setUri( + String.format( + "cloudspanner://localhost:%d/projects/proj/instances/inst/databases/db?credentialsProvider=%s", + getPort(), TestCredentialsProvider.class.getName())) + .setConfigurator( + spannerOptions -> + spannerOptions.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)) + .build(); + try (Connection connection = options.getConnection()) { + assertEquals( + TestCredentials.class, + ((ConnectionImpl) connection).getSpanner().getOptions().getCredentials().getClass()); + TestCredentials credentials = + (TestCredentials) + ((ConnectionImpl) connection).getSpanner().getOptions().getCredentials(); + assertEquals(2, credentials.id); + } + } +} From c0de54d483592e49c54f232bc24a3631fac5d66e Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 10 May 2022 11:32:56 +0530 Subject: [PATCH 24/24] chore(main): release 6.24.0 (#1846) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 21 +++++++++++++++++++ google-cloud-spanner-bom/pom.xml | 18 ++++++++-------- google-cloud-spanner/pom.xml | 4 ++-- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- grpc-google-cloud-spanner-v1/pom.xml | 4 ++-- pom.xml | 16 +++++++------- .../pom.xml | 4 ++-- .../pom.xml | 4 ++-- proto-google-cloud-spanner-v1/pom.xml | 4 ++-- samples/snapshot/pom.xml | 2 +- versions.txt | 14 ++++++------- 12 files changed, 60 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd510305ad3..4d3609ba587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## [6.24.0](https://github.com/googleapis/java-spanner/compare/v6.23.3...v6.24.0) (2022-05-05) + + +### Features + +* Copy backup samples ([#1802](https://github.com/googleapis/java-spanner/issues/1802)) ([787ccad](https://github.com/googleapis/java-spanner/commit/787ccadcba01193d541bfd1b80b055fb5d4c2bb3)) +* support CREATE DATABASE in Connection API ([#1845](https://github.com/googleapis/java-spanner/issues/1845)) ([40110fe](https://github.com/googleapis/java-spanner/commit/40110feb22986c6b5dac6885eae7f0b331aede61)) +* support CredentialsProvider in Connection API ([#1869](https://github.com/googleapis/java-spanner/issues/1869)) ([f1d2d3e](https://github.com/googleapis/java-spanner/commit/f1d2d3ef1dbd30c153616c2efcc362c1330705e1)) + + +### Dependencies + +* update dependency com.google.cloud:google-cloud-monitoring to v3.2.8 ([#1831](https://github.com/googleapis/java-spanner/issues/1831)) ([088fb50](https://github.com/googleapis/java-spanner/commit/088fb50a673a99e6921503be0f84b8291173240e)) +* update dependency com.google.cloud:google-cloud-monitoring to v3.2.9 ([#1851](https://github.com/googleapis/java-spanner/issues/1851)) ([4d6bb2d](https://github.com/googleapis/java-spanner/commit/4d6bb2dd233fba60d213d36f15aead67dff57dec)) +* update dependency com.google.cloud:google-cloud-trace to v2.1.11 ([#1799](https://github.com/googleapis/java-spanner/issues/1799)) ([049635d](https://github.com/googleapis/java-spanner/commit/049635d4bc3210bd9ce41444f17c8b9d67af969a)) + + +### Documentation + +* add samples for PostgresSQL ([#1781](https://github.com/googleapis/java-spanner/issues/1781)) ([e832298](https://github.com/googleapis/java-spanner/commit/e8322986f158a86cdbb04332a9c49ead79fb2587)) + ### [6.23.3](https://github.com/googleapis/java-spanner/compare/v6.23.2...v6.23.3) (2022-04-21) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index 2ffb0bca97c..3c4da8c7caa 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner-bom - 6.23.4-SNAPSHOT + 6.24.0 pom com.google.cloud @@ -53,43 +53,43 @@ com.google.cloud google-cloud-spanner - 6.23.4-SNAPSHOT + 6.24.0 com.google.cloud google-cloud-spanner test-jar - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.23.4-SNAPSHOT + 6.24.0 diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 96510c7f504..3c364f6a572 100644 --- a/google-cloud-spanner/pom.xml +++ b/google-cloud-spanner/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner - 6.23.4-SNAPSHOT + 6.24.0 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 6.23.4-SNAPSHOT + 6.24.0 google-cloud-spanner diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index b4ebdd640f9..41a0ea926ed 100644 --- a/grpc-google-cloud-spanner-admin-database-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.23.4-SNAPSHOT + 6.24.0 grpc-google-cloud-spanner-admin-database-v1 GRPC library for grpc-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.23.4-SNAPSHOT + 6.24.0 diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index d3601e5528c..24c25239d42 100644 --- a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.23.4-SNAPSHOT + 6.24.0 grpc-google-cloud-spanner-admin-instance-v1 GRPC library for grpc-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.23.4-SNAPSHOT + 6.24.0 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 5620bdeb730..db36a9d41a3 100644 --- a/grpc-google-cloud-spanner-v1/pom.xml +++ b/grpc-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.23.4-SNAPSHOT + 6.24.0 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.23.4-SNAPSHOT + 6.24.0 diff --git a/pom.xml b/pom.xml index 6f81a5acc31..939f8bdcece 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 6.23.4-SNAPSHOT + 6.24.0 Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -62,37 +62,37 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.23.4-SNAPSHOT + 6.24.0 com.google.cloud google-cloud-spanner - 6.23.4-SNAPSHOT + 6.24.0 diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index 966df0b52bb..9cd0d6b5d52 100644 --- a/proto-google-cloud-spanner-admin-database-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.23.4-SNAPSHOT + 6.24.0 proto-google-cloud-spanner-admin-database-v1 PROTO library for proto-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.23.4-SNAPSHOT + 6.24.0 diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index 621d2b1528f..fbf0723d588 100644 --- a/proto-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.23.4-SNAPSHOT + 6.24.0 proto-google-cloud-spanner-admin-instance-v1 PROTO library for proto-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.23.4-SNAPSHOT + 6.24.0 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index 5357b897bd6..65357382211 100644 --- a/proto-google-cloud-spanner-v1/pom.xml +++ b/proto-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.23.4-SNAPSHOT + 6.24.0 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.23.4-SNAPSHOT + 6.24.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 2868f893ab4..7a99556e4bc 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -31,7 +31,7 @@ com.google.cloud google-cloud-spanner - 6.23.4-SNAPSHOT + 6.24.0 diff --git a/versions.txt b/versions.txt index c2576a0a8d8..109c09bd8aa 100644 --- a/versions.txt +++ b/versions.txt @@ -1,10 +1,10 @@ # Format: # module:released-version:current-version -proto-google-cloud-spanner-admin-instance-v1:6.23.3:6.23.4-SNAPSHOT -proto-google-cloud-spanner-v1:6.23.3:6.23.4-SNAPSHOT -proto-google-cloud-spanner-admin-database-v1:6.23.3:6.23.4-SNAPSHOT -grpc-google-cloud-spanner-v1:6.23.3:6.23.4-SNAPSHOT -grpc-google-cloud-spanner-admin-instance-v1:6.23.3:6.23.4-SNAPSHOT -grpc-google-cloud-spanner-admin-database-v1:6.23.3:6.23.4-SNAPSHOT -google-cloud-spanner:6.23.3:6.23.4-SNAPSHOT +proto-google-cloud-spanner-admin-instance-v1:6.24.0:6.24.0 +proto-google-cloud-spanner-v1:6.24.0:6.24.0 +proto-google-cloud-spanner-admin-database-v1:6.24.0:6.24.0 +grpc-google-cloud-spanner-v1:6.24.0:6.24.0 +grpc-google-cloud-spanner-admin-instance-v1:6.24.0:6.24.0 +grpc-google-cloud-spanner-admin-database-v1:6.24.0:6.24.0 +google-cloud-spanner:6.24.0:6.24.0