From 77f649bfa5ac4fdcbef51fadd52b85ea6b1437f2 Mon Sep 17 00:00:00 2001
From: "release-please[bot]"
<55107282+release-please[bot]@users.noreply.github.com>
Date: Fri, 23 Feb 2024 05:54:16 +0000
Subject: [PATCH 01/16] chore(main): release 6.60.2-SNAPSHOT (#2922)
:robot: I have created a release *beep* *boop*
---
### Updating meta-information for bleeding-edge SNAPSHOT release.
---
This PR was generated with [Release Please](https://togithub.com/googleapis/release-please). See [documentation](https://togithub.com/googleapis/release-please#release-please).
---
google-cloud-spanner-bom/pom.xml | 18 ++++++++---------
google-cloud-spanner-executor/pom.xml | 4 ++--
google-cloud-spanner/pom.xml | 4 ++--
.../pom.xml | 4 ++--
.../pom.xml | 4 ++--
grpc-google-cloud-spanner-executor-v1/pom.xml | 4 ++--
grpc-google-cloud-spanner-v1/pom.xml | 4 ++--
pom.xml | 20 +++++++++----------
.../pom.xml | 4 ++--
.../pom.xml | 4 ++--
.../pom.xml | 4 ++--
proto-google-cloud-spanner-v1/pom.xml | 4 ++--
samples/snapshot/pom.xml | 2 +-
versions.txt | 20 +++++++++----------
14 files changed, 50 insertions(+), 50 deletions(-)
diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml
index 0ef292d4ca..db147dd9dc 100644
--- a/google-cloud-spanner-bom/pom.xml
+++ b/google-cloud-spanner-bom/pom.xml
@@ -3,7 +3,7 @@
4.0.0com.google.cloudgoogle-cloud-spanner-bom
- 6.60.1
+ 6.60.2-SNAPSHOTpomcom.google.cloud
@@ -53,43 +53,43 @@
com.google.cloudgoogle-cloud-spanner
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.cloudgoogle-cloud-spannertest-jar
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcgrpc-google-cloud-spanner-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcgrpc-google-cloud-spanner-admin-instance-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcgrpc-google-cloud-spanner-admin-database-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcproto-google-cloud-spanner-admin-instance-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcproto-google-cloud-spanner-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcproto-google-cloud-spanner-admin-database-v1
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/google-cloud-spanner-executor/pom.xml b/google-cloud-spanner-executor/pom.xml
index 2d563185aa..a8a8f96719 100644
--- a/google-cloud-spanner-executor/pom.xml
+++ b/google-cloud-spanner-executor/pom.xml
@@ -5,14 +5,14 @@
4.0.0com.google.cloudgoogle-cloud-spanner-executor
- 6.60.1
+ 6.60.2-SNAPSHOTjarGoogle Cloud Spanner Executorcom.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml
index ef121f7a32..3c17a2a0de 100644
--- a/google-cloud-spanner/pom.xml
+++ b/google-cloud-spanner/pom.xml
@@ -3,7 +3,7 @@
4.0.0com.google.cloudgoogle-cloud-spanner
- 6.60.1
+ 6.60.2-SNAPSHOTjarGoogle Cloud Spannerhttps://github.com/googleapis/java-spanner
@@ -11,7 +11,7 @@
com.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOTgoogle-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 fe0aae9d55..b1677e6299 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.0com.google.api.grpcgrpc-google-cloud-spanner-admin-database-v1
- 6.60.1
+ 6.60.2-SNAPSHOTgrpc-google-cloud-spanner-admin-database-v1GRPC library for grpc-google-cloud-spanner-admin-database-v1com.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml
index 7d4e7ec962..2e6b57966c 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.0com.google.api.grpcgrpc-google-cloud-spanner-admin-instance-v1
- 6.60.1
+ 6.60.2-SNAPSHOTgrpc-google-cloud-spanner-admin-instance-v1GRPC library for grpc-google-cloud-spanner-admin-instance-v1com.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/grpc-google-cloud-spanner-executor-v1/pom.xml b/grpc-google-cloud-spanner-executor-v1/pom.xml
index 5d508b1cc2..0572282d5d 100644
--- a/grpc-google-cloud-spanner-executor-v1/pom.xml
+++ b/grpc-google-cloud-spanner-executor-v1/pom.xml
@@ -4,13 +4,13 @@
4.0.0com.google.api.grpcgrpc-google-cloud-spanner-executor-v1
- 6.60.1
+ 6.60.2-SNAPSHOTgrpc-google-cloud-spanner-executor-v1GRPC library for google-cloud-spannercom.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml
index 399a04ef7b..7595a5a451 100644
--- a/grpc-google-cloud-spanner-v1/pom.xml
+++ b/grpc-google-cloud-spanner-v1/pom.xml
@@ -4,13 +4,13 @@
4.0.0com.google.api.grpcgrpc-google-cloud-spanner-v1
- 6.60.1
+ 6.60.2-SNAPSHOTgrpc-google-cloud-spanner-v1GRPC library for grpc-google-cloud-spanner-v1com.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/pom.xml b/pom.xml
index ef95e28fbf..1bc7455aa3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.google.cloudgoogle-cloud-spanner-parentpom
- 6.60.1
+ 6.60.2-SNAPSHOTGoogle Cloud Spanner Parenthttps://github.com/googleapis/java-spanner
@@ -61,47 +61,47 @@
com.google.api.grpcproto-google-cloud-spanner-admin-instance-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcproto-google-cloud-spanner-executor-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcgrpc-google-cloud-spanner-executor-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcproto-google-cloud-spanner-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcproto-google-cloud-spanner-admin-database-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcgrpc-google-cloud-spanner-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcgrpc-google-cloud-spanner-admin-instance-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.api.grpcgrpc-google-cloud-spanner-admin-database-v1
- 6.60.1
+ 6.60.2-SNAPSHOTcom.google.cloudgoogle-cloud-spanner
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml
index 0514be006b..752aae6b95 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.0com.google.api.grpcproto-google-cloud-spanner-admin-database-v1
- 6.60.1
+ 6.60.2-SNAPSHOTproto-google-cloud-spanner-admin-database-v1PROTO library for proto-google-cloud-spanner-admin-database-v1com.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml
index 9fe5dad3a6..626c0c8f13 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.0com.google.api.grpcproto-google-cloud-spanner-admin-instance-v1
- 6.60.1
+ 6.60.2-SNAPSHOTproto-google-cloud-spanner-admin-instance-v1PROTO library for proto-google-cloud-spanner-admin-instance-v1com.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/proto-google-cloud-spanner-executor-v1/pom.xml b/proto-google-cloud-spanner-executor-v1/pom.xml
index 4e560822d1..eb6669fb12 100644
--- a/proto-google-cloud-spanner-executor-v1/pom.xml
+++ b/proto-google-cloud-spanner-executor-v1/pom.xml
@@ -4,13 +4,13 @@
4.0.0com.google.api.grpcproto-google-cloud-spanner-executor-v1
- 6.60.1
+ 6.60.2-SNAPSHOTproto-google-cloud-spanner-executor-v1Proto library for google-cloud-spannercom.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml
index dafe631172..e87c94e5aa 100644
--- a/proto-google-cloud-spanner-v1/pom.xml
+++ b/proto-google-cloud-spanner-v1/pom.xml
@@ -4,13 +4,13 @@
4.0.0com.google.api.grpcproto-google-cloud-spanner-v1
- 6.60.1
+ 6.60.2-SNAPSHOTproto-google-cloud-spanner-v1PROTO library for proto-google-cloud-spanner-v1com.google.cloudgoogle-cloud-spanner-parent
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index cc31362f1a..82ce69cd8e 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -32,7 +32,7 @@
com.google.cloudgoogle-cloud-spanner
- 6.60.1
+ 6.60.2-SNAPSHOT
diff --git a/versions.txt b/versions.txt
index debc27fc48..8d82efe3b9 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,13 +1,13 @@
# Format:
# module:released-version:current-version
-proto-google-cloud-spanner-admin-instance-v1:6.60.1:6.60.1
-proto-google-cloud-spanner-v1:6.60.1:6.60.1
-proto-google-cloud-spanner-admin-database-v1:6.60.1:6.60.1
-grpc-google-cloud-spanner-v1:6.60.1:6.60.1
-grpc-google-cloud-spanner-admin-instance-v1:6.60.1:6.60.1
-grpc-google-cloud-spanner-admin-database-v1:6.60.1:6.60.1
-google-cloud-spanner:6.60.1:6.60.1
-google-cloud-spanner-executor:6.60.1:6.60.1
-proto-google-cloud-spanner-executor-v1:6.60.1:6.60.1
-grpc-google-cloud-spanner-executor-v1:6.60.1:6.60.1
+proto-google-cloud-spanner-admin-instance-v1:6.60.1:6.60.2-SNAPSHOT
+proto-google-cloud-spanner-v1:6.60.1:6.60.2-SNAPSHOT
+proto-google-cloud-spanner-admin-database-v1:6.60.1:6.60.2-SNAPSHOT
+grpc-google-cloud-spanner-v1:6.60.1:6.60.2-SNAPSHOT
+grpc-google-cloud-spanner-admin-instance-v1:6.60.1:6.60.2-SNAPSHOT
+grpc-google-cloud-spanner-admin-database-v1:6.60.1:6.60.2-SNAPSHOT
+google-cloud-spanner:6.60.1:6.60.2-SNAPSHOT
+google-cloud-spanner-executor:6.60.1:6.60.2-SNAPSHOT
+proto-google-cloud-spanner-executor-v1:6.60.1:6.60.2-SNAPSHOT
+grpc-google-cloud-spanner-executor-v1:6.60.1:6.60.2-SNAPSHOT
From 704ad74df9b54d0c0d387d977bdb52fa1c597559 Mon Sep 17 00:00:00 2001
From: Mend Renovate
Date: Fri, 23 Feb 2024 13:20:56 +0100
Subject: [PATCH 02/16] chore(deps): update dependency
com.google.cloud:google-cloud-spanner to v6.60.1 (#2923)
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.60.1
* 🦉 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 | 8 ++++----
samples/install-without-bom/pom.xml | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 257ec20bc1..74d113f00d 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ If you are using Maven without the BOM, add this to your dependencies:
com.google.cloudgoogle-cloud-spanner
- 6.60.0
+ 6.60.1
```
@@ -57,13 +57,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.60.0'
+implementation 'com.google.cloud:google-cloud-spanner:6.60.1'
```
If you are using SBT, add this to your dependencies:
```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.60.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.60.1"
```
@@ -643,7 +643,7 @@ Java is a registered trademark of Oracle and/or its affiliates.
[kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-spanner/java11.html
[stability-image]: https://img.shields.io/badge/stability-stable-green
[maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-spanner.svg
-[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.60.0
+[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.60.1
[authentication]: https://github.com/googleapis/google-cloud-java#authentication
[auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes
[predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index af217d9bd4..0ad21eebfc 100644
--- a/samples/install-without-bom/pom.xml
+++ b/samples/install-without-bom/pom.xml
@@ -33,7 +33,7 @@
com.google.cloudgoogle-cloud-spanner
- 6.60.0
+ 6.60.1
From de1363645e03f46deed5be41f90ddfed72766751 Mon Sep 17 00:00:00 2001
From: Arpan Mishra
Date: Fri, 23 Feb 2024 19:05:24 +0530
Subject: [PATCH 03/16] docs: samples and tests for backup Admin APIs and
overall spanner Admin APIs. (#2882)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* fix: prevent illegal negative timeout values into thread sleep() method while retrying exceptions in unit tests.
* For details on issue see - https://github.com/googleapis/java-spanner/issues/2206
* Fixing lint issues.
* chore: copy samples and rewrite create backup.
* chore: fix code.
* chore: fix code.
* chore: fix code.
* chore: fix error.
* fix: all compilation errors.
* fix: more compilation issues.
* fix: all compile issues.
* chore: compile and test PgSpannerSample.
* fix: all issues with PgSpannerSample.
* chore: compile and fix SpannerSample/SpannerSampleIT.
* chore: fix kms configs and ITs.
* chore: fix cancel backup tests.
* Update samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java
Co-authored-by: Knut Olav Løite
* chore: add log statements in tests.
* chore: fix lint errors and fix comments.
* chore: fix lint errors.
* chore: fix lint errors.
* chore: fix backup samples for restore use-case.
* fix: fix restore/list backup tests.
* chore: fix PgSpannerSample sample and test.
* chore: fix delete backup test.
* chore: fix lint errors.
* 🦉 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
* chore: add IT for CopyBackupSample.
* chore: fix lint errors.
* chore: refactor test for delete backup.
---------
Co-authored-by: Knut Olav Løite
Co-authored-by: Owl Bot
---
README.md | 6 +
samples/install-without-bom/pom.xml | 4 +-
samples/snapshot/pom.xml | 4 +-
samples/snippets/pom.xml | 4 +-
.../admin/generated/CopyBackupSample.java | 94 +
.../CreateBackupWithEncryptionKey.java | 111 +
.../CreateDatabaseWithEncryptionKey.java | 99 +
.../admin/generated/PgSpannerSample.java | 1626 ++++++++++++
.../RestoreBackupWithEncryptionKey.java | 88 +
.../admin/generated/SpannerSample.java | 2275 +++++++++++++++++
.../spanner/admin/generated/CopyBackupIT.java | 112 +
.../admin/generated/EncryptionKeyIT.java | 123 +
.../admin/generated/PgSpannerSampleIT.java | 289 +++
.../admin/generated/SpannerSampleIT.java | 694 +++++
14 files changed, 5523 insertions(+), 6 deletions(-)
create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java
create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java
create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java
create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java
create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java
create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java
create mode 100644 samples/snippets/src/test/java/com/example/spanner/admin/generated/CopyBackupIT.java
create mode 100644 samples/snippets/src/test/java/com/example/spanner/admin/generated/EncryptionKeyIT.java
create mode 100644 samples/snippets/src/test/java/com/example/spanner/admin/generated/PgSpannerSampleIT.java
create mode 100644 samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java
diff --git a/README.md b/README.md
index 74d113f00d..2ed6ff04b4 100644
--- a/README.md
+++ b/README.md
@@ -515,6 +515,9 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/
| Add Numeric Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddNumericColumnSample.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/admin/generated/AddNumericColumnSample.java) |
| Alter Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterSequenceSample.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/admin/generated/AlterSequenceSample.java) |
| Alter Table With Foreign Key Delete Cascade Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSample.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/admin/generated/AlterTableWithForeignKeyDeleteCascadeSample.java) |
+| Copy Backup Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/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/admin/generated/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/admin/generated/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/admin/generated/CreateBackupWithEncryptionKey.java) |
+| Create Database With Encryption Key | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/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/admin/generated/CreateDatabaseWithEncryptionKey.java) |
| Create Database With Version Retention Period Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSample.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/admin/generated/CreateDatabaseWithVersionRetentionPeriodSample.java) |
| Create Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceConfigSample.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/admin/generated/CreateInstanceConfigSample.java) |
| Create Instance Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceExample.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/admin/generated/CreateInstanceExample.java) |
@@ -537,6 +540,9 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/
| Pg Create Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCreateSequenceSample.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/admin/generated/PgCreateSequenceSample.java) |
| Pg Drop Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgDropSequenceSample.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/admin/generated/PgDropSequenceSample.java) |
| Pg Interleaved Table Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/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/admin/generated/PgInterleavedTableSample.java) |
+| Pg Spanner Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/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/admin/generated/PgSpannerSample.java) |
+| Restore Backup With Encryption Key | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.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/admin/generated/RestoreBackupWithEncryptionKey.java) |
+| Spanner Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.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/admin/generated/SpannerSample.java) |
| Update Database Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseSample.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/admin/generated/UpdateDatabaseSample.java) |
| Update Database With Default Leader Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSample.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/admin/generated/UpdateDatabaseWithDefaultLeaderSample.java) |
| Update Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateInstanceConfigSample.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/admin/generated/UpdateInstanceConfigSample.java) |
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index 0ad21eebfc..1d990d82e2 100644
--- a/samples/install-without-bom/pom.xml
+++ b/samples/install-without-bom/pom.xml
@@ -147,8 +147,8 @@
java-client-mr-integration-testnam11us-east1
- cmek-test-key-ring
- cmek-test-key
+ java-client-integration-test-cmek-ring
+ java-client-integration-test-cmek-keymysamplequick-db
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 82ce69cd8e..d14be6eabd 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -146,8 +146,8 @@
java-client-mr-integration-testnam11us-east1
- cmek-test-key-ring
- cmek-test-key
+ java-client-integration-test-cmek-ring
+ java-client-integration-test-cmek-keymysamplemysample-instancequick-db
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 5dbe22f146..e8d2155b33 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -182,8 +182,8 @@
java-client-mr-integration-testnam11us-east1
- cmek-test-key-ring
- cmek-test-key
+ java-client-integration-test-cmek-ring
+ java-client-integration-test-cmek-keymysamplequick-db
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java
new file mode 100644
index 0000000000..ebac469ca7
--- /dev/null
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java
@@ -0,0 +1,94 @@
+/*
+ * 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.admin.generated;
+
+// [START spanner_copy_backup]
+
+import com.google.cloud.Timestamp;
+import com.google.cloud.spanner.SpannerException;
+import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.spanner.admin.database.v1.Backup;
+import com.google.spanner.admin.database.v1.BackupName;
+import com.google.spanner.admin.database.v1.InstanceName;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+public class CopyBackupSample {
+
+ static void copyBackup() throws IOException {
+ // 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 (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
+ 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));
+
+ // Initiate the request which returns an OperationFuture.
+ System.out.println("Copying backup [" + destinationBackupId + "]...");
+ Backup destinationBackup;
+ try {
+ // Creates a copy of an existing backup.
+ // Wait for the backup operation to complete.
+ destinationBackup = databaseAdminClient.copyBackupAsync(
+ InstanceName.of(projectId, instanceId), destinationBackupId,
+ BackupName.of(projectId, instanceId, sourceBackupId), expireTime.toProto()).get();
+ System.out.println("Copied backup [" + destinationBackup.getName() + "]");
+ } 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 = databaseAdminClient.getBackup(destinationBackup.getName());
+ System.out.println(
+ String.format(
+ "Backup %s of size %d bytes was copied at %s for version of database at %s",
+ destinationBackup.getName(),
+ destinationBackup.getSizeBytes(),
+ OffsetDateTime.ofInstant(
+ Instant.ofEpochSecond(destinationBackup.getCreateTime().getSeconds(),
+ destinationBackup.getCreateTime().getNanos()),
+ ZoneId.systemDefault()),
+ OffsetDateTime.ofInstant(
+ Instant.ofEpochSecond(destinationBackup.getVersionTime().getSeconds(),
+ destinationBackup.getVersionTime().getNanos()),
+ ZoneId.systemDefault())));
+ }
+}
+// [END spanner_copy_backup]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java
new file mode 100644
index 0000000000..39f070e639
--- /dev/null
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.spanner.admin.generated;
+
+// [START spanner_create_backup_with_encryption_key]
+
+import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.cloud.spanner.encryption.EncryptionConfigs;
+import com.google.protobuf.Timestamp;
+import com.google.spanner.admin.database.v1.Backup;
+import com.google.spanner.admin.database.v1.BackupName;
+import com.google.spanner.admin.database.v1.CreateBackupEncryptionConfig;
+import com.google.spanner.admin.database.v1.CreateBackupEncryptionConfig.EncryptionType;
+import com.google.spanner.admin.database.v1.CreateBackupMetadata;
+import com.google.spanner.admin.database.v1.CreateBackupRequest;
+import com.google.spanner.admin.database.v1.DatabaseName;
+import com.google.spanner.admin.database.v1.InstanceName;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.threeten.bp.LocalDateTime;
+import org.threeten.bp.OffsetDateTime;
+
+public class CreateBackupWithEncryptionKey {
+
+ static void createBackupWithEncryptionKey() throws IOException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectId = "my-project";
+ String instanceId = "my-instance";
+ String databaseId = "my-database";
+ String backupId = "my-backup";
+ String kmsKeyName =
+ "projects/" + projectId + "/locations//keyRings//cryptoKeys/";
+
+ try (DatabaseAdminClient adminClient = DatabaseAdminClient.create()) {
+ createBackupWithEncryptionKey(
+ adminClient,
+ projectId,
+ instanceId,
+ databaseId,
+ backupId,
+ kmsKeyName);
+ }
+ }
+
+ static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient,
+ String projectId, String instanceId, String databaseId, String backupId, String kmsKeyName) {
+ // Set expire time to 14 days from now.
+ final Timestamp expireTime =
+ Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds((
+ System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14)))).build();
+ final BackupName backupName = BackupName.of(projectId, instanceId, backupId);
+ Backup backup = Backup.newBuilder()
+ .setName(backupName.toString())
+ .setDatabase(DatabaseName.of(projectId, instanceId, databaseId).toString())
+ .setExpireTime(expireTime).build();
+
+ final CreateBackupRequest request =
+ CreateBackupRequest.newBuilder()
+ .setParent(InstanceName.of(projectId, instanceId).toString())
+ .setBackupId(backupId)
+ .setBackup(backup)
+ .setEncryptionConfig(
+ CreateBackupEncryptionConfig.newBuilder()
+ .setEncryptionType(EncryptionType.CUSTOMER_MANAGED_ENCRYPTION)
+ .setKmsKeyName(kmsKeyName).build()).build();
+ try {
+ System.out.println("Waiting for operation to complete...");
+ backup = adminClient.createBackupAsync(request).get(1200, TimeUnit.SECONDS);
+ } catch (ExecutionException e) {
+ // If the operation failed during execution, expose the cause.
+ throw SpannerExceptionFactory.asSpannerException(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);
+ } catch (TimeoutException e) {
+ // If the operation timed out propagates the timeout
+ throw SpannerExceptionFactory.propagateTimeout(e);
+ }
+ System.out.printf(
+ "Backup %s of size %d bytes was created at %s using encryption key %s%n",
+ backup.getName(),
+ backup.getSizeBytes(),
+ LocalDateTime.ofEpochSecond(
+ backup.getCreateTime().getSeconds(),
+ backup.getCreateTime().getNanos(),
+ OffsetDateTime.now().getOffset()),
+ kmsKeyName
+ );
+
+ return null;
+ }
+}
+// [END spanner_create_backup_with_encryption_key]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java
new file mode 100644
index 0000000000..2e9d9889f4
--- /dev/null
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.spanner.admin.generated;
+
+// [START spanner_create_database_with_encryption_key]
+
+import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.common.collect.ImmutableList;
+import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
+import com.google.spanner.admin.database.v1.Database;
+import com.google.spanner.admin.database.v1.EncryptionConfig;
+import com.google.spanner.admin.database.v1.InstanceName;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class CreateDatabaseWithEncryptionKey {
+
+ static void createDatabaseWithEncryptionKey() throws IOException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectId = "my-project";
+ String instanceId = "my-instance";
+ String databaseId = "my-database";
+ String kmsKeyName =
+ "projects/" + projectId + "/locations//keyRings//cryptoKeys/";
+
+ try (DatabaseAdminClient adminClient = DatabaseAdminClient.create()) {
+ createDatabaseWithEncryptionKey(
+ adminClient,
+ projectId,
+ instanceId,
+ databaseId,
+ kmsKeyName);
+ }
+ }
+
+ static void createDatabaseWithEncryptionKey(DatabaseAdminClient adminClient,
+ String projectId, String instanceId, String databaseId, String kmsKeyName) {
+ InstanceName instanceName = InstanceName.of(projectId, instanceId);
+ CreateDatabaseRequest request = CreateDatabaseRequest.newBuilder()
+ .setParent(instanceName.toString())
+ .setCreateStatement("CREATE DATABASE `" + databaseId + "`")
+ .setEncryptionConfig(EncryptionConfig.newBuilder().setKmsKeyName(kmsKeyName).build())
+ .addAllExtraStatements(
+ ImmutableList.of(
+ "CREATE TABLE Singers ("
+ + " SingerId INT64 NOT NULL,"
+ + " FirstName STRING(1024),"
+ + " LastName STRING(1024),"
+ + " SingerInfo BYTES(MAX)"
+ + ") PRIMARY KEY (SingerId)",
+ "CREATE TABLE Albums ("
+ + " SingerId INT64 NOT NULL,"
+ + " AlbumId INT64 NOT NULL,"
+ + " AlbumTitle STRING(MAX)"
+ + ") PRIMARY KEY (SingerId, AlbumId),"
+ + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE"
+ ))
+ .build();
+ try {
+ System.out.println("Waiting for operation to complete...");
+ Database createdDatabase =
+ adminClient.createDatabaseAsync(request).get(120, TimeUnit.SECONDS);
+
+ System.out.printf(
+ "Database %s created with encryption key %s%n",
+ createdDatabase.getName(),
+ createdDatabase.getEncryptionConfig().getKmsKeyName()
+ );
+ } catch (ExecutionException e) {
+ // If the operation failed during execution, expose the cause.
+ throw SpannerExceptionFactory.asSpannerException(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);
+ } catch (TimeoutException e) {
+ // If the operation timed out propagates the timeout
+ throw SpannerExceptionFactory.propagateTimeout(e);
+ }
+ }
+}
+// [END spanner_create_database_with_encryption_key]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java
new file mode 100644
index 0000000000..b2e6eafb0f
--- /dev/null
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java
@@ -0,0 +1,1626 @@
+/*
+ * 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.admin.generated;
+
+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.DatabaseClient;
+import com.google.cloud.spanner.DatabaseId;
+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.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListBackupOperationsPagedResponse;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListDatabaseOperationsPagedResponse;
+import com.google.common.io.BaseEncoding;
+import com.google.longrunning.Operation;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.spanner.admin.database.v1.BackupName;
+import com.google.spanner.admin.database.v1.CopyBackupMetadata;
+import com.google.spanner.admin.database.v1.CreateBackupMetadata;
+import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
+import com.google.spanner.admin.database.v1.Database;
+import com.google.spanner.admin.database.v1.DatabaseDialect;
+import com.google.spanner.admin.database.v1.DatabaseName;
+import com.google.spanner.admin.database.v1.ListBackupOperationsRequest;
+import com.google.spanner.admin.database.v1.ListDatabaseOperationsRequest;
+import com.google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata;
+import com.google.spanner.admin.instance.v1.InstanceName;
+import com.google.spanner.v1.ExecuteSqlRequest;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+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 {
+
+ // [START spanner_postgresql_insert_data]
+ 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"));
+ // [END spanner_postgresql_insert_data]
+
+ /**
+ * 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, String projectId, String instanceId, String databaseId) {
+ final CreateDatabaseRequest request =
+ CreateDatabaseRequest.newBuilder()
+ .setCreateStatement("CREATE DATABASE \"" + databaseId + "\"")
+ .setParent(InstanceName.of(projectId, instanceId).toString())
+ .setDatabaseDialect(DatabaseDialect.POSTGRESQL).build();
+
+ try {
+ // Initiate the request which returns an OperationFuture.
+ Database db = dbAdminClient.createDatabaseAsync(request).get();
+ System.out.println("Created database [" + db.getName() + "]");
+ } 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, DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ adminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget bigint")).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, DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ adminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)")).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, DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ adminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList(
+ "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) "
+ + "INCLUDE (MarketingBudget)")).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]
+ // [START spanner_postgresql_create_database]
+ static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ dbAdminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList(
+ "CREATE TABLE Singers ("
+ + " SingerId bigint NOT NULL,"
+ + " FirstName character varying(1024),"
+ + " LastName character varying(1024),"
+ + " SingerInfo bytea,"
+ + " FullName character varying(2048) GENERATED "
+ + " ALWAYS AS (FirstName || ' ' || LastName) STORED,"
+ + " 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")).get();
+ System.out.println("Created Singers & Albums tables in database: [" + databaseName + "]");
+ } catch (ExecutionException e) {
+ // If the operation failed during execution, expose the cause.
+ throw SpannerExceptionFactory.asSpannerException(e);
+ } 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]
+ // [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, DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ adminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList(
+ "ALTER TABLE Albums ADD COLUMN LastUpdateTime spanner.commit_timestamp")).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,
+ DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ dbAdminClient.updateDatabaseDdlAsync(databaseName,
+ 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")).get();
+ System.out.println("Created Performances table in database: [" + databaseName + "]");
+ } 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,
+ DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ dbAdminClient.updateDatabaseDdlAsync(
+ databaseName,
+ 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))")).get();
+ System.out.println("Created Venues table in database: [" + databaseName + "]");
+ } 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(
+ DatabaseAdminClient databaseAdminClient,
+ String projectId, String instanceId,
+ String databaseId, String backupId) {
+ com.google.spanner.admin.database.v1.InstanceName instanceName =
+ com.google.spanner.admin.database.v1.InstanceName.of(projectId, instanceId);
+ // Get 'CreateBackup' operations for the sample database.
+ String filter =
+ String.format(
+ "(metadata.@type:type.googleapis.com/"
+ + "google.spanner.admin.database.v1.CreateBackupMetadata) "
+ + "AND (metadata.database:%s)",
+ DatabaseName.of(projectId, instanceId, databaseId).toString());
+ ListBackupOperationsRequest listBackupOperationsRequest =
+ ListBackupOperationsRequest.newBuilder()
+ .setParent(instanceName.toString()).setFilter(filter).build();
+ ListBackupOperationsPagedResponse createBackupOperations
+ = databaseAdminClient.listBackupOperations(listBackupOperationsRequest);
+ System.out.println("Create Backup Operations:");
+ for (Operation op : createBackupOperations.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());
+ }
+ }
+ // 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)",
+ BackupName.of(projectId, instanceId, backupId).toString());
+ listBackupOperationsRequest =
+ ListBackupOperationsRequest.newBuilder()
+ .setParent(instanceName.toString()).setFilter(filter).build();
+ ListBackupOperationsPagedResponse copyBackupOperations =
+ databaseAdminClient.listBackupOperations(listBackupOperationsRequest);
+ 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_postgresql_list_backup_operations]
+
+ // [START spanner_postgresql_list_database_operations]
+ static void listDatabaseOperations(
+ DatabaseAdminClient dbAdminClient, String projectId, String instanceId) {
+ // Get optimize restored database operations.
+ com.google.cloud.Timestamp last24Hours = com.google.cloud.Timestamp.ofTimeSecondsAndNanos(
+ TimeUnit.SECONDS.convert(
+ TimeUnit.HOURS.convert(com.google.cloud.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);
+ ListDatabaseOperationsRequest listDatabaseOperationsRequest =
+ ListDatabaseOperationsRequest.newBuilder()
+ .setParent(com.google.spanner.admin.instance.v1.InstanceName.of(
+ projectId, instanceId).toString()).setFilter(filter).build();
+ ListDatabaseOperationsPagedResponse pagedResponse
+ = dbAdminClient.listDatabaseOperations(listDatabaseOperationsRequest);
+ for (Operation op : pagedResponse.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,
+ String command,
+ DatabaseId database,
+ String backupId) {
+ DatabaseName databaseName = DatabaseName.of(database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase());
+ switch (command) {
+ case "createpgdatabase":
+ createPostgreSqlDatabase(dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase());
+ 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, databaseName);
+ break;
+ case "update":
+ update(dbClient);
+ break;
+ case "writetransaction":
+ writeWithTransaction(dbClient);
+ break;
+ case "querymarketingbudget":
+ queryMarketingBudget(dbClient);
+ break;
+ case "addindex":
+ addIndex(dbAdminClient, databaseName);
+ break;
+ case "readindex":
+ readUsingIndex(dbClient);
+ break;
+ case "addstoringindex":
+ addStoringIndex(dbAdminClient, databaseName);
+ 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, databaseName);
+ break;
+ case "readstaledata":
+ readStaleData(dbClient);
+ break;
+ case "addlastupdatetimestampcolumn":
+ addLastUpdateTimestampColumn(dbAdminClient, databaseName);
+ break;
+ case "updatewithtimestamp":
+ updateWithTimestamp(dbClient);
+ break;
+ case "querywithtimestamp":
+ queryMarketingBudgetWithTimestamp(dbClient);
+ break;
+ case "createtablewithtimestamp":
+ createTableWithTimestamp(dbAdminClient, databaseName);
+ 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, databaseName);
+ 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(dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase(), backupId);
+ break;
+ case "listdatabaseoperations":
+ listDatabaseOperations(dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance());
+ 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 createdatabase 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) throws Exception {
+ if (args.length != 3) {
+ printUsageAndExit();
+ }
+ // [START spanner_init_client]
+ SpannerOptions options = SpannerOptions.newBuilder().build();
+ Spanner spanner = options.getService();
+ DatabaseAdminClient dbAdminClient = null;
+ try {
+ // [END spanner_init_client]
+ final 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();
+ }
+ // Generate a backup id for the sample database.
+ String backupId = null;
+ if (args.length == 4) {
+ backupId = args[3];
+ }
+
+ // [START spanner_init_client]
+ DatabaseClient dbClient = spanner.getDatabaseClient(db);
+ dbAdminClient = DatabaseAdminClient.create();
+ // [END spanner_init_client]
+
+ // Use client here...
+ run(dbClient, dbAdminClient, command, db, backupId);
+ // [START spanner_init_client]
+ } finally {
+ if (dbAdminClient != null) {
+ if (!dbAdminClient.isShutdown() || !dbAdminClient.isTerminated()) {
+ dbAdminClient.close();
+ }
+ }
+ 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/admin/generated/RestoreBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java
new file mode 100644
index 0000000000..9c2ef5b3a7
--- /dev/null
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.spanner.admin.generated;
+
+// [START spanner_restore_backup_with_encryption_key]
+
+import static com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION;
+
+import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.spanner.admin.database.v1.BackupName;
+import com.google.spanner.admin.database.v1.Database;
+import com.google.spanner.admin.database.v1.DatabaseName;
+import com.google.spanner.admin.database.v1.InstanceName;
+import com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig;
+import com.google.spanner.admin.database.v1.RestoreDatabaseRequest;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+public class RestoreBackupWithEncryptionKey {
+
+ static void restoreBackupWithEncryptionKey() throws IOException {
+ // TODO(developer): Replace these variables before running the sample.
+ String projectId = "my-project";
+ String instanceId = "my-instance";
+ String databaseId = "my-database";
+ String backupId = "my-backup";
+ String kmsKeyName =
+ "projects/" + projectId + "/locations//keyRings//cryptoKeys/";
+
+ try (DatabaseAdminClient adminClient = DatabaseAdminClient.create()) {
+ restoreBackupWithEncryptionKey(
+ adminClient,
+ projectId,
+ instanceId,
+ backupId,
+ databaseId,
+ kmsKeyName);
+ }
+ }
+
+ static Void restoreBackupWithEncryptionKey(DatabaseAdminClient adminClient,
+ String projectId, String instanceId, String backupId, String restoreId, String kmsKeyName) {
+ RestoreDatabaseRequest request =
+ RestoreDatabaseRequest.newBuilder()
+ .setParent(InstanceName.of(projectId, instanceId).toString())
+ .setDatabaseId(restoreId)
+ .setBackup(BackupName.of(projectId, instanceId, backupId).toString())
+ .setEncryptionConfig(RestoreDatabaseEncryptionConfig.newBuilder()
+ .setEncryptionType(CUSTOMER_MANAGED_ENCRYPTION).setKmsKeyName(kmsKeyName)).build();
+ Database database;
+ try {
+ System.out.println("Waiting for operation to complete...");
+ database = adminClient.restoreDatabaseAsync(request).get();;
+ } catch (ExecutionException e) {
+ // If the operation failed during execution, expose the cause.
+ throw SpannerExceptionFactory.asSpannerException(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);
+ }
+
+ System.out.printf(
+ "Database %s restored to %s from backup %s using encryption key %s%n",
+ database.getRestoreInfo().getBackupInfo().getSourceDatabase(),
+ database.getName(),
+ database.getRestoreInfo().getBackupInfo().getBackup(),
+ database.getEncryptionConfig().getKmsKeyName()
+ );
+ return null;
+ }
+}
+// [END spanner_restore_backup_with_encryption_key]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java
new file mode 100644
index 0000000000..6e805dc132
--- /dev/null
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java
@@ -0,0 +1,2275 @@
+/*
+ * Copyright 2017 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.admin.generated;
+
+import static com.google.cloud.spanner.Type.StructField;
+
+import com.google.api.gax.longrunning.OperationFuture;
+import com.google.api.gax.longrunning.OperationSnapshot;
+import com.google.api.gax.retrying.RetryingFuture;
+import com.google.api.gax.rpc.NotFoundException;
+import com.google.api.gax.rpc.StatusCode;
+import com.google.api.gax.rpc.StatusCode.Code;
+import com.google.cloud.ByteArray;
+import com.google.cloud.Date;
+import com.google.cloud.spanner.DatabaseClient;
+import com.google.cloud.spanner.DatabaseId;
+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.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.Type;
+import com.google.cloud.spanner.Value;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListBackupOperationsPagedResponse;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListBackupsPagedResponse;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListDatabaseOperationsPagedResponse;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.io.BaseEncoding;
+import com.google.longrunning.Operation;
+import com.google.protobuf.FieldMask;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Timestamp;
+import com.google.spanner.admin.database.v1.Backup;
+import com.google.spanner.admin.database.v1.BackupInfo;
+import com.google.spanner.admin.database.v1.BackupName;
+import com.google.spanner.admin.database.v1.CopyBackupMetadata;
+import com.google.spanner.admin.database.v1.CreateBackupMetadata;
+import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
+import com.google.spanner.admin.database.v1.DatabaseName;
+import com.google.spanner.admin.database.v1.InstanceName;
+import com.google.spanner.admin.database.v1.ListBackupOperationsRequest;
+import com.google.spanner.admin.database.v1.ListBackupsRequest;
+import com.google.spanner.admin.database.v1.ListDatabaseOperationsRequest;
+import com.google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata;
+import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata;
+import com.google.spanner.admin.database.v1.RestoreDatabaseRequest;
+import com.google.spanner.admin.database.v1.RestoreInfo;
+import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Example code for using the Cloud Spanner API. This example demonstrates all the common operations
+ * that can be done on Cloud Spanner. These are:
+ *
+ *
+ *
+ *
+ *
Creating a Cloud Spanner database.
+ *
Writing, reading and executing SQL queries.
+ *
Writing data using a read-write transaction.
+ *
Using an index to read and execute SQL queries over data.
+ *
Using commit timestamp for tracking when a record was last updated.
+ *
Using Google API Extensions for Java to make thread-safe requests via long-running
+ * operations. http://googleapis.github.io/gax-java/
+ *
+ */
+public class SpannerSample {
+
+ /**
+ * 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;
+ }
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ /**
+ * 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;
+ final Value venueDetails;
+
+ Venue(
+ long venueId,
+ String venueName,
+ String venueInfo,
+ long capacity,
+ Value availableDates,
+ String lastContactDate,
+ boolean outdoorVenue,
+ float popularityScore,
+ BigDecimal revenue,
+ Value venueDetails) {
+ 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;
+ this.venueDetails = venueDetails;
+ }
+ }
+
+ // [START spanner_insert_data]
+ 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"));
+ // [END spanner_insert_data]
+
+ // [START spanner_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));
+ // [END spanner_insert_data_with_timestamp_column]
+
+ // [START spanner_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")));
+ 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"),
+ Value.json(
+ "[{\"name\":\"room 1\",\"open\":true},{\"name\":\"room 2\",\"open\":false}]")),
+ new Venue(
+ 19,
+ "Venue 19",
+ exampleBytes2,
+ 6300,
+ availableDates2,
+ "2019-01-15",
+ true,
+ 0.98716f,
+ new BigDecimal("1200100.00"),
+ Value.json("{\"rating\":9,\"open\":true}")),
+ new Venue(
+ 42,
+ "Venue 42",
+ exampleBytes3,
+ 3000,
+ availableDates3,
+ "2018-10-01",
+ false,
+ 0.72598f,
+ new BigDecimal("390650.99"),
+ Value.json(
+ "{\"name\":null,"
+ + "\"open\":{\"Monday\":true,\"Tuesday\":false},"
+ + "\"tags\":[\"large\",\"airy\"]}")));
+ // [END spanner_insert_datatypes_data]
+
+ // [START spanner_create_database]
+ static void createDatabase(DatabaseAdminClient dbAdminClient,
+ InstanceName instanceName, String databaseId) {
+ CreateDatabaseRequest createDatabaseRequest =
+ CreateDatabaseRequest.newBuilder()
+ .setCreateStatement("CREATE DATABASE `" + databaseId + "`")
+ .setParent(instanceName.toString())
+ .addAllExtraStatements(Arrays.asList(
+ "CREATE TABLE Singers ("
+ + " SingerId INT64 NOT NULL,"
+ + " FirstName STRING(1024),"
+ + " LastName STRING(1024),"
+ + " SingerInfo BYTES(MAX),"
+ + " FullName STRING(2048) AS "
+ + " (ARRAY_TO_STRING([FirstName, LastName], \" \")) STORED"
+ + ") PRIMARY KEY (SingerId)",
+ "CREATE TABLE Albums ("
+ + " SingerId INT64 NOT NULL,"
+ + " AlbumId INT64 NOT NULL,"
+ + " AlbumTitle STRING(MAX)"
+ + ") PRIMARY KEY (SingerId, AlbumId),"
+ + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")).build();
+ try {
+ // Initiate the request which returns an OperationFuture.
+ com.google.spanner.admin.database.v1.Database db =
+ dbAdminClient.createDatabaseAsync(createDatabaseRequest).get();
+ System.out.println("Created database [" + db.getName() + "]");
+ } 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_create_database]
+
+ // [START spanner_create_table_with_timestamp_column]
+ static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient,
+ DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ dbAdminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList(
+ "CREATE TABLE Performances ("
+ + " SingerId INT64 NOT NULL,"
+ + " VenueId INT64 NOT NULL,"
+ + " EventDate Date,"
+ + " Revenue INT64, "
+ + " LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)"
+ + ") PRIMARY KEY (SingerId, VenueId, EventDate),"
+ + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")).get();
+ System.out.println(
+ "Created Performances table in database: [" + databaseName.toString() + "]");
+ } 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_create_table_with_timestamp_column]
+
+ // [START spanner_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("EventDate")
+ .to(performance.eventDate)
+ .set("Revenue")
+ .to(performance.revenue)
+ .set("LastUpdateTime")
+ .to(Value.COMMIT_TIMESTAMP)
+ .build());
+ }
+ dbClient.write(mutations);
+ }
+ // [END spanner_insert_data_with_timestamp_column]
+
+ // [START spanner_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_insert_data]
+
+ // [START spanner_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_delete_data]
+
+ // [START spanner_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_query_data]
+
+ // [START spanner_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_read_data]
+
+ // [START spanner_add_column]
+ static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ adminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget INT64")).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_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_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_update_data]
+
+ // [START spanner_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_read_write_transaction]
+
+ // [START spanner_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, AlbumId, 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_query_data_with_new_column]
+
+ // [START spanner_create_index]
+ static void addIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ adminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)")).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_create_index]
+
+ // Before running this example, add the index AlbumsByAlbumTitle by applying the DDL statement
+ // "CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)".
+ // [START spanner_query_data_with_index]
+ static void queryUsingIndex(DatabaseClient dbClient) {
+ Statement statement =
+ Statement
+ // We use FORCE_INDEX hint to specify which index to use. For more details see
+ // https://cloud.google.com/spanner/docs/query-syntax#from-clause
+ .newBuilder(
+ "SELECT AlbumId, AlbumTitle, MarketingBudget "
+ + "FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} "
+ + "WHERE AlbumTitle >= @StartTitle AND AlbumTitle < @EndTitle")
+ // We use @BoundParameters to help speed up frequently executed queries.
+ // For more details see https://cloud.google.com/spanner/docs/sql-best-practices
+ .bind("StartTitle")
+ .to("Aardvark")
+ .bind("EndTitle")
+ .to("Goo")
+ .build();
+ try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %s %s\n",
+ resultSet.getLong("AlbumId"),
+ resultSet.getString("AlbumTitle"),
+ resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"));
+ }
+ }
+ }
+ // [END spanner_query_data_with_index]
+
+ // [START spanner_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_read_data_with_index]
+
+ // [START spanner_create_storing_index]
+ static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ adminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList(
+ "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) "
+ + "STORING (MarketingBudget)")).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_create_storing_index]
+
+ // Before running this example, create a storing index AlbumsByAlbumTitle2 by applying the DDL
+ // statement "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)".
+ // [START spanner_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("MarketingBudget"));
+ }
+ }
+ }
+ // [END spanner_read_data_with_storing_index]
+
+ // [START spanner_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_read_only_transaction]
+
+ // [START spanner_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("MarketingBudget"));
+ }
+ }
+ }
+ // [END spanner_read_stale_data]
+
+ // [START spanner_add_timestamp_column]
+ static void addCommitTimestamp(DatabaseAdminClient adminClient, DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ adminClient.updateDatabaseDdlAsync(
+ databaseName,
+ Arrays.asList(
+ "ALTER TABLE Albums ADD COLUMN LastUpdateTime TIMESTAMP "
+ + "OPTIONS (allow_commit_timestamp=true)")).get();
+ System.out.println("Added LastUpdateTime as a commit 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_add_timestamp_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".
+ // In addition this update expects the LastUpdateTime column added by applying the DDL statement
+ // "ALTER TABLE Albums ADD COLUMN LastUpdateTime TIMESTAMP OPTIONS (allow_commit_timestamp=true)"
+ // [START spanner_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_update_data_with_timestamp_column]
+
+ // [START spanner_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, AlbumId, MarketingBudget, 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_query_data_with_timestamp_column]
+
+ static void querySingersTable(DatabaseClient dbClient) {
+ try (ResultSet resultSet =
+ dbClient
+ .singleUse()
+ .executeQuery(Statement.of("SELECT SingerId, FirstName, LastName FROM Singers"))) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%s %s %s\n",
+ resultSet.getLong("SingerId"),
+ resultSet.getString("FirstName"),
+ resultSet.getString("LastName"));
+ }
+ }
+ }
+
+ 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, VenueId, EventDate, Revenue, LastUpdateTime "
+ + "FROM Performances ORDER BY LastUpdateTime DESC"))) {
+ while (resultSet.next()) {
+ System.out.printf(
+ "%d %d %s %s %s\n",
+ resultSet.getLong("SingerId"),
+ resultSet.getLong("VenueId"),
+ resultSet.getDate("EventDate"),
+ // 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_write_data_for_struct_queries]
+ static void writeStructExampleData(DatabaseClient dbClient) {
+ final List singers =
+ Arrays.asList(
+ new Singer(6, "Elena", "Campbell"),
+ new Singer(7, "Gabriel", "Wright"),
+ new Singer(8, "Benjamin", "Martinez"),
+ new Singer(9, "Hannah", "Harris"));
+
+ 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());
+ }
+ dbClient.write(mutations);
+ System.out.println("Inserted example data for struct parameter queries.");
+ }
+ // [END spanner_write_data_for_struct_queries]
+
+ static void queryWithStruct(DatabaseClient dbClient) {
+ // [START spanner_create_struct_with_data]
+ Struct name =
+ Struct.newBuilder().set("FirstName").to("Elena").set("LastName").to("Campbell").build();
+ // [END spanner_create_struct_with_data]
+
+ // [START spanner_query_data_with_struct]
+ Statement s =
+ Statement.newBuilder(
+ "SELECT SingerId FROM Singers "
+ + "WHERE STRUCT(FirstName, LastName) "
+ + "= @name")
+ .bind("name")
+ .to(name)
+ .build();
+ try (ResultSet resultSet = dbClient.singleUse().executeQuery(s)) {
+ while (resultSet.next()) {
+ System.out.printf("%d\n", resultSet.getLong("SingerId"));
+ }
+ }
+ // [END spanner_query_data_with_struct]
+ }
+
+ static void queryWithArrayOfStruct(DatabaseClient dbClient) {
+ // [START spanner_create_user_defined_struct]
+ Type nameType =
+ Type.struct(
+ Arrays.asList(
+ StructField.of("FirstName", Type.string()),
+ StructField.of("LastName", Type.string())));
+ // [END spanner_create_user_defined_struct]
+
+ // [START spanner_create_array_of_struct_with_data]
+ List bandMembers = new ArrayList<>();
+ bandMembers.add(
+ Struct.newBuilder().set("FirstName").to("Elena").set("LastName").to("Campbell").build());
+ bandMembers.add(
+ Struct.newBuilder().set("FirstName").to("Gabriel").set("LastName").to("Wright").build());
+ bandMembers.add(
+ Struct.newBuilder().set("FirstName").to("Benjamin").set("LastName").to("Martinez").build());
+ // [END spanner_create_array_of_struct_with_data]
+
+ // [START spanner_query_data_with_array_of_struct]
+ Statement s =
+ Statement.newBuilder(
+ "SELECT SingerId FROM Singers WHERE "
+ + "STRUCT(FirstName, LastName) "
+ + "IN UNNEST(@names) "
+ + "ORDER BY SingerId DESC")
+ .bind("names")
+ .toStructArray(nameType, bandMembers)
+ .build();
+ try (ResultSet resultSet = dbClient.singleUse().executeQuery(s)) {
+ while (resultSet.next()) {
+ System.out.printf("%d\n", resultSet.getLong("SingerId"));
+ }
+ }
+ // [END spanner_query_data_with_array_of_struct]
+ }
+
+ // [START spanner_field_access_on_struct_parameters]
+ static void queryStructField(DatabaseClient dbClient) {
+ Statement s =
+ Statement.newBuilder("SELECT SingerId FROM Singers WHERE FirstName = @name.FirstName")
+ .bind("name")
+ .to(
+ Struct.newBuilder()
+ .set("FirstName")
+ .to("Elena")
+ .set("LastName")
+ .to("Campbell")
+ .build())
+ .build();
+ try (ResultSet resultSet = dbClient.singleUse().executeQuery(s)) {
+ while (resultSet.next()) {
+ System.out.printf("%d\n", resultSet.getLong("SingerId"));
+ }
+ }
+ }
+ // [END spanner_field_access_on_struct_parameters]
+
+ // [START spanner_field_access_on_nested_struct_parameters]
+ static void queryNestedStructField(DatabaseClient dbClient) {
+ Type nameType =
+ Type.struct(
+ Arrays.asList(
+ StructField.of("FirstName", Type.string()),
+ StructField.of("LastName", Type.string())));
+
+ Struct songInfo =
+ Struct.newBuilder()
+ .set("song_name")
+ .to("Imagination")
+ .set("artistNames")
+ .toStructArray(
+ nameType,
+ Arrays.asList(
+ Struct.newBuilder()
+ .set("FirstName")
+ .to("Elena")
+ .set("LastName")
+ .to("Campbell")
+ .build(),
+ Struct.newBuilder()
+ .set("FirstName")
+ .to("Hannah")
+ .set("LastName")
+ .to("Harris")
+ .build()))
+ .build();
+ Statement s =
+ Statement.newBuilder(
+ "SELECT SingerId, @song_info.song_name "
+ + "FROM Singers WHERE "
+ + "STRUCT(FirstName, LastName) "
+ + "IN UNNEST(@song_info.artistNames)")
+ .bind("song_info")
+ .to(songInfo)
+ .build();
+ try (ResultSet resultSet = dbClient.singleUse().executeQuery(s)) {
+ while (resultSet.next()) {
+ System.out.printf("%d %s\n", resultSet.getLong("SingerId"), resultSet.getString(1));
+ }
+ }
+ }
+ // [END spanner_field_access_on_nested_struct_parameters]
+
+ // [START spanner_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_dml_standard_insert]
+
+ // [START spanner_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_dml_standard_update]
+
+ // [START spanner_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_dml_standard_delete]
+
+ // [START spanner_dml_standard_update_with_timestamp]
+ static void updateUsingDmlWithTimestamp(DatabaseClient dbClient) {
+ dbClient
+ .readWriteTransaction()
+ .run(transaction -> {
+ String sql =
+ "UPDATE Albums "
+ + "SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1";
+ long rowCount = transaction.executeUpdate(Statement.of(sql));
+ System.out.printf("%d records updated.\n", rowCount);
+ return null;
+ });
+ }
+ // [END spanner_dml_standard_update_with_timestamp]
+
+ // [START spanner_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, 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_dml_write_then_read]
+
+ // [START spanner_dml_structs]
+ static void updateUsingDmlWithStruct(DatabaseClient dbClient) {
+ Struct name =
+ Struct.newBuilder().set("FirstName").to("Timothy").set("LastName").to("Campbell").build();
+ Statement s =
+ Statement.newBuilder(
+ "UPDATE Singers SET LastName = 'Grant' "
+ + "WHERE STRUCT(FirstName, LastName) "
+ + "= @name")
+ .bind("name")
+ .to(name)
+ .build();
+ dbClient
+ .readWriteTransaction()
+ .run(transaction -> {
+ long rowCount = transaction.executeUpdate(s);
+ System.out.printf("%d record updated.\n", rowCount);
+ return null;
+ });
+ }
+ // [END spanner_dml_structs]
+
+ // [START spanner_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_dml_getting_started_insert]
+
+ // [START spanner_query_with_parameter]
+ static void queryWithParameter(DatabaseClient dbClient) {
+ Statement statement =
+ Statement.newBuilder(
+ "SELECT SingerId, FirstName, LastName "
+ + "FROM Singers "
+ + "WHERE LastName = @lastName")
+ .bind("lastName")
+ .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_query_with_parameter]
+
+ // [START spanner_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 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 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 = @AlbumBudget "
+ + "WHERE SingerId = 1 and AlbumId = 1")
+ .bind("AlbumBudget")
+ .to(album1Budget)
+ .build();
+ transaction.executeUpdate(updateStatement);
+ Statement updateStatement2 =
+ Statement.newBuilder(
+ "UPDATE Albums "
+ + "SET MarketingBudget = @AlbumBudget "
+ + "WHERE SingerId = 2 and AlbumId = 2")
+ .bind("AlbumBudget")
+ .to(album2Budget)
+ .build();
+ transaction.executeUpdate(updateStatement2);
+ }
+ return null;
+ });
+ }
+ // [END spanner_dml_getting_started_update]
+
+ // [START spanner_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_dml_partitioned_update]
+
+ // [START spanner_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_dml_partitioned_delete]
+
+ // [START spanner_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_dml_batch_update]
+
+ // [START spanner_create_table_with_datatypes]
+ static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient,
+ DatabaseName databaseName) {
+ try {
+ // Initiate the request which returns an OperationFuture.
+ dbAdminClient.updateDatabaseDdlAsync(databaseName,
+ Arrays.asList(
+ "CREATE TABLE Venues ("
+ + " VenueId INT64 NOT NULL,"
+ + " VenueName STRING(100),"
+ + " VenueInfo BYTES(MAX),"
+ + " Capacity INT64,"
+ + " AvailableDates ARRAY,"
+ + " LastContactDate DATE,"
+ + " OutdoorVenue BOOL, "
+ + " PopularityScore FLOAT64, "
+ + " Revenue NUMERIC, "
+ + " VenueDetails JSON, "
+ + " LastUpdateTime TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)"
+ + ") PRIMARY KEY (VenueId)")).get();
+ System.out.println("Created Venues table in database: [" + databaseName.toString() + "]");
+ } 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_create_table_with_datatypes]
+
+ // [START spanner_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("AvailableDates")
+ .to(venue.availableDates)
+ .set("LastContactDate")
+ .to(venue.lastContactDate)
+ .set("OutdoorVenue")
+ .to(venue.outdoorVenue)
+ .set("PopularityScore")
+ .to(venue.popularityScore)
+ .set("Revenue")
+ .to(venue.revenue)
+ .set("VenueDetails")
+ .to(venue.venueDetails)
+ .set("LastUpdateTime")
+ .to(Value.COMMIT_TIMESTAMP)
+ .build());
+ }
+ dbClient.write(mutations);
+ }
+ // [END spanner_insert_datatypes_data]
+
+ // [START spanner_query_with_array_parameter]
+ static void queryWithArray(DatabaseClient dbClient) {
+ Value exampleArray =
+ Value.dateArray(Arrays.asList(Date.parseDate("2020-10-01"), Date.parseDate("2020-11-01")));
+
+ Statement statement =
+ Statement.newBuilder(
+ "SELECT VenueId, VenueName, AvailableDate FROM Venues v, "
+ + "UNNEST(v.AvailableDates) as AvailableDate "
+ + "WHERE AvailableDate in UNNEST(@availableDates)")
+ .bind("availableDates")
+ .to(exampleArray)
+ .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.getDate("AvailableDate"));
+ }
+ }
+ }
+ // [END spanner_query_with_array_parameter]
+
+ // [START spanner_query_with_bool_parameter]
+ static void queryWithBool(DatabaseClient dbClient) {
+ boolean exampleBool = true;
+ Statement statement =
+ Statement.newBuilder(
+ "SELECT VenueId, VenueName, OutdoorVenue FROM Venues "
+ + "WHERE OutdoorVenue = @outdoorVenue")
+ .bind("outdoorVenue")
+ .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_query_with_bool_parameter]
+
+ // [START spanner_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, VenueName FROM Venues " + "WHERE VenueInfo = @venueInfo")
+ .bind("venueInfo")
+ .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_query_with_bytes_parameter]
+
+ // [START spanner_query_with_date_parameter]
+ static void queryWithDate(DatabaseClient dbClient) {
+ String exampleDate = "2019-01-01";
+ Statement statement =
+ Statement.newBuilder(
+ "SELECT VenueId, VenueName, LastContactDate FROM Venues "
+ + "WHERE LastContactDate < @lastContactDate")
+ .bind("lastContactDate")
+ .to(exampleDate)
+ .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.getDate("LastContactDate"));
+ }
+ }
+ }
+ // [END spanner_query_with_date_parameter]
+
+ // [START spanner_query_with_float_parameter]
+ static void queryWithFloat(DatabaseClient dbClient) {
+ float exampleFloat = 0.8f;
+ Statement statement =
+ Statement.newBuilder(
+ "SELECT VenueId, VenueName, PopularityScore FROM Venues "
+ + "WHERE PopularityScore > @popularityScore")
+ .bind("popularityScore")
+ .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_query_with_float_parameter]
+
+ // [START spanner_query_with_int_parameter]
+ static void queryWithInt(DatabaseClient dbClient) {
+ long exampleInt = 3000;
+ Statement statement =
+ Statement.newBuilder(
+ "SELECT VenueId, VenueName, Capacity FROM Venues " + "WHERE Capacity >= @capacity")
+ .bind("capacity")
+ .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_query_with_int_parameter]
+
+ // [START spanner_query_with_string_parameter]
+ static void queryWithString(DatabaseClient dbClient) {
+ String exampleString = "Venue 42";
+ Statement statement =
+ Statement.newBuilder(
+ "SELECT VenueId, VenueName FROM Venues " + "WHERE VenueName = @venueName")
+ .bind("venueName")
+ .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_query_with_string_parameter]
+
+ // [START spanner_query_with_timestamp_parameter]
+ static void queryWithTimestampParameter(DatabaseClient dbClient) {
+ Instant exampleTimestamp = Instant.now();
+ Statement statement =
+ Statement.newBuilder(
+ "SELECT VenueId, VenueName, LastUpdateTime FROM Venues "
+ + "WHERE LastUpdateTime < @lastUpdateTime")
+ .bind("lastUpdateTime")
+ .to(exampleTimestamp.toString())
+ .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_query_with_timestamp_parameter]
+
+ // [START spanner_query_with_numeric_parameter]
+ static void queryWithNumeric(DatabaseClient dbClient) {
+ BigDecimal exampleNumeric = new BigDecimal("300000");
+ Statement statement =
+ Statement.newBuilder(
+ "SELECT VenueId, VenueName, Revenue\n"
+ + "FROM Venues\n"
+ + "WHERE Revenue >= @revenue")
+ .bind("revenue")
+ .to(exampleNumeric)
+ .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.getBigDecimal("Revenue"));
+ }
+ }
+ }
+ // [END spanner_query_with_numeric_parameter]
+
+ // [START spanner_create_client_with_query_options]
+ static void clientWithQueryOptions(DatabaseId db) {
+ SpannerOptions options =
+ SpannerOptions.newBuilder()
+ .setDefaultQueryOptions(
+ db, QueryOptions
+ .newBuilder()
+ .setOptimizerVersion("1")
+ // The list of available statistics packages can be found by querying the
+ // "INFORMATION_SCHEMA.SPANNER_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_create_client_with_query_options]
+
+ // [START spanner_query_with_query_options]
+ static void queryWithQueryOptions(DatabaseClient dbClient) {
+ try (ResultSet resultSet =
+ dbClient
+ .singleUse()
+ .executeQuery(
+ Statement
+ .newBuilder("SELECT SingerId, AlbumId, AlbumTitle FROM Albums")
+ .withQueryOptions(QueryOptions
+ .newBuilder()
+ .setOptimizerVersion("1")
+ // The list of available statistics packages can be found by querying the
+ // "INFORMATION_SCHEMA.SPANNER_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_query_with_query_options]
+
+ // [START spanner_create_backup]
+ static void createBackup(DatabaseAdminClient dbAdminClient, String projectId, String instanceId,
+ String databaseId, String backupId, Timestamp versionTime) {
+ // Set expire time to 14 days from now.
+ Timestamp expireTime =
+ Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds((
+ System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14)))).build();
+ BackupName backupName = BackupName.of(projectId, instanceId, backupId);
+ Backup backup = Backup.newBuilder()
+ .setName(backupName.toString())
+ .setDatabase(DatabaseName.of(projectId, instanceId, databaseId).toString())
+ .setExpireTime(expireTime).setVersionTime(versionTime).build();
+
+ // Initiate the request which returns an OperationFuture.
+ System.out.println("Creating backup [" + backupId + "]...");
+ try {
+ // Wait for the backup operation to complete.
+ backup = dbAdminClient.createBackupAsync(
+ InstanceName.of(projectId, instanceId), backup, backupId).get();
+ System.out.println("Created backup [" + backup.getName() + "]");
+ } catch (ExecutionException e) {
+ throw SpannerExceptionFactory.asSpannerException(e);
+ } catch (InterruptedException e) {
+ throw SpannerExceptionFactory.propagateInterrupt(e);
+ }
+
+ // Reload the metadata of the backup from the server.
+ backup = dbAdminClient.getBackup(backup.getName());
+ System.out.println(
+ String.format(
+ "Backup %s of size %d bytes was created at %s for version of database at %s",
+ backup.getName(),
+ backup.getSizeBytes(),
+ java.time.OffsetDateTime.ofInstant(
+ Instant.ofEpochSecond(backup.getCreateTime().getSeconds(),
+ backup.getCreateTime().getNanos()), ZoneId.systemDefault()),
+ java.time.OffsetDateTime.ofInstant(
+ Instant.ofEpochSecond(backup.getVersionTime().getSeconds(),
+ backup.getVersionTime().getNanos()), ZoneId.systemDefault()))
+ );
+ }
+ // [END spanner_create_backup]
+
+ // [START spanner_cancel_backup_create]
+ static void cancelCreateBackup(
+ DatabaseAdminClient dbAdminClient, String projectId, String instanceId,
+ String databaseId, String backupId) {
+ // Set expire time to 14 days from now.
+ Timestamp expireTime =
+ Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds((
+ System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14)))).build();
+ BackupName backupName = BackupName.of(projectId, instanceId, backupId);
+ Backup backup = Backup.newBuilder()
+ .setName(backupName.toString())
+ .setDatabase(DatabaseName.of(projectId, instanceId, databaseId).toString())
+ .setExpireTime(expireTime).build();
+
+ try {
+ // Start the creation of a backup.
+ System.out.println("Creating backup [" + backupId + "]...");
+ OperationFuture op = dbAdminClient.createBackupAsync(
+ InstanceName.of(projectId, instanceId), backup, backupId);
+
+ // Try to cancel the backup operation.
+ System.out.println("Cancelling create backup operation for [" + backupId + "]...");
+ dbAdminClient.getOperationsClient().cancelOperation(op.getName());
+
+ // Get a polling future for the running operation. This future will regularly poll the server
+ // for the current status of the backup operation.
+ RetryingFuture pollingFuture = op.getPollingFuture();
+
+ // Wait for the operation to finish.
+ // isDone will return true when the operation is complete, regardless of whether it was
+ // successful or not.
+ while (!pollingFuture.get().isDone()) {
+ System.out.println("Waiting for the cancelled backup operation to finish...");
+ Thread.sleep(TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS));
+ }
+ if (pollingFuture.get().getErrorCode() == null) {
+ // Backup was created before it could be cancelled. Delete the backup.
+ dbAdminClient.deleteBackup(backupName);
+ System.out.println("Backup operation for [" + backupId
+ + "] successfully finished before it could be cancelled");
+ } else if (pollingFuture.get().getErrorCode().getCode() == StatusCode.Code.CANCELLED) {
+ System.out.println("Backup operation for [" + backupId + "] successfully cancelled");
+ }
+ } catch (ExecutionException e) {
+ throw SpannerExceptionFactory.newSpannerException(e.getCause());
+ } catch (InterruptedException e) {
+ throw SpannerExceptionFactory.propagateInterrupt(e);
+ }
+ }
+ // [END spanner_cancel_backup_create]
+
+ // [START spanner_list_backup_operations]
+ static void listBackupOperations(
+ DatabaseAdminClient databaseAdminClient,
+ String projectId, String instanceId,
+ String databaseId, String backupId) {
+ InstanceName instanceName = InstanceName.of(projectId, instanceId);
+ // Get 'CreateBackup' operations for the sample database.
+ String filter =
+ String.format(
+ "(metadata.@type:type.googleapis.com/"
+ + "google.spanner.admin.database.v1.CreateBackupMetadata) "
+ + "AND (metadata.database:%s)",
+ DatabaseName.of(projectId, instanceId, databaseId).toString());
+ ListBackupOperationsRequest listBackupOperationsRequest =
+ ListBackupOperationsRequest.newBuilder()
+ .setParent(instanceName.toString()).setFilter(filter).build();
+ ListBackupOperationsPagedResponse createBackupOperations
+ = databaseAdminClient.listBackupOperations(listBackupOperationsRequest);
+ System.out.println("Create Backup Operations:");
+ for (Operation op : createBackupOperations.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());
+ }
+ }
+ // 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)",
+ BackupName.of(projectId, instanceId, backupId).toString());
+ listBackupOperationsRequest =
+ ListBackupOperationsRequest.newBuilder()
+ .setParent(instanceName.toString()).setFilter(filter).build();
+ ListBackupOperationsPagedResponse copyBackupOperations =
+ databaseAdminClient.listBackupOperations(listBackupOperationsRequest);
+ 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]
+
+ // [START spanner_list_database_operations]
+ static void listDatabaseOperations(
+ DatabaseAdminClient dbAdminClient, String projectId, String instanceId) {
+ // Get optimize restored database operations.
+ com.google.cloud.Timestamp last24Hours = com.google.cloud.Timestamp.ofTimeSecondsAndNanos(
+ TimeUnit.SECONDS.convert(
+ TimeUnit.HOURS.convert(com.google.cloud.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);
+ ListDatabaseOperationsRequest listDatabaseOperationsRequest =
+ ListDatabaseOperationsRequest.newBuilder()
+ .setParent(com.google.spanner.admin.instance.v1.InstanceName.of(
+ projectId, instanceId).toString()).setFilter(filter).build();
+ ListDatabaseOperationsPagedResponse pagedResponse
+ = dbAdminClient.listDatabaseOperations(listDatabaseOperationsRequest);
+ for (Operation op : pagedResponse.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_list_database_operations]
+
+ // [START spanner_list_backups]
+ static void listBackups(
+ DatabaseAdminClient dbAdminClient, String projectId,
+ String instanceId, String databaseId, String backupId) {
+ InstanceName instanceName = InstanceName.of(projectId, instanceId);
+ // List all backups.
+ System.out.println("All backups:");
+ for (Backup backup : dbAdminClient.listBackups(
+ instanceName.toString()).iterateAll()) {
+ System.out.println(backup);
+ }
+
+ // List all backups with a specific name.
+ System.out.println(
+ String.format("All backups with backup name containing \"%s\":", backupId));
+ ListBackupsRequest listBackupsRequest =
+ ListBackupsRequest.newBuilder().setParent(instanceName.toString())
+ .setFilter(String.format("name:%s", backupId)).build();
+ for (Backup backup : dbAdminClient.listBackups(listBackupsRequest).iterateAll()) {
+ System.out.println(backup);
+ }
+
+ // List all backups for databases whose name contains a certain text.
+ System.out.println(
+ String.format(
+ "All backups for databases with a name containing \"%s\":", databaseId));
+ listBackupsRequest =
+ ListBackupsRequest.newBuilder().setParent(instanceName.toString())
+ .setFilter(String.format("database:%s", databaseId)).build();
+ for (Backup backup : dbAdminClient.listBackups(listBackupsRequest).iterateAll()) {
+ System.out.println(backup);
+ }
+
+ // List all backups that expire before a certain time.
+ com.google.cloud.Timestamp expireTime = com.google.cloud.Timestamp.ofTimeMicroseconds(
+ TimeUnit.MICROSECONDS.convert(
+ System.currentTimeMillis() + TimeUnit.DAYS.toMillis(30), TimeUnit.MILLISECONDS));
+
+ System.out.println(String.format("All backups that expire before %s:", expireTime));
+ listBackupsRequest =
+ ListBackupsRequest.newBuilder().setParent(instanceName.toString())
+ .setFilter(String.format("expire_time < \"%s\"", expireTime)).build();
+
+ for (Backup backup : dbAdminClient.listBackups(listBackupsRequest).iterateAll()) {
+ System.out.println(backup);
+ }
+
+ // List all backups with size greater than a certain number of bytes.
+ listBackupsRequest =
+ ListBackupsRequest.newBuilder().setParent(instanceName.toString())
+ .setFilter("size_bytes > 100").build();
+
+ System.out.println("All backups with size greater than 100 bytes:");
+ for (Backup backup : dbAdminClient.listBackups(listBackupsRequest).iterateAll()) {
+ System.out.println(backup);
+ }
+
+ // List all backups with a create time after a certain timestamp and that are also ready.
+ com.google.cloud.Timestamp createTime = com.google.cloud.Timestamp.ofTimeMicroseconds(
+ TimeUnit.MICROSECONDS.convert(
+ System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1), TimeUnit.MILLISECONDS));
+
+ System.out.println(
+ String.format(
+ "All databases created after %s and that are ready:", createTime.toString()));
+ listBackupsRequest =
+ ListBackupsRequest.newBuilder().setParent(instanceName.toString())
+ .setFilter(String.format(
+ "create_time >= \"%s\" AND state:READY", createTime.toString())).build();
+ for (Backup backup : dbAdminClient.listBackups(listBackupsRequest).iterateAll()) {
+ System.out.println(backup);
+ }
+
+ // List backups using pagination.
+ System.out.println("All backups, listed using pagination:");
+ listBackupsRequest =
+ ListBackupsRequest.newBuilder().setParent(instanceName.toString()).setPageSize(10).build();
+ while (true) {
+ ListBackupsPagedResponse response = dbAdminClient.listBackups(listBackupsRequest);
+ for (Backup backup : response.getPage().iterateAll()) {
+ System.out.println(backup);
+ }
+ String nextPageToken = response.getNextPageToken();
+ if (!Strings.isNullOrEmpty(nextPageToken)) {
+ listBackupsRequest = listBackupsRequest.toBuilder().setPageToken(nextPageToken).build();
+ } else {
+ break;
+ }
+ }
+ }
+ // [END spanner_list_backups]
+
+ // [START spanner_restore_backup]
+ static void restoreBackup(
+ DatabaseAdminClient dbAdminClient,
+ String projectId,
+ String instanceId,
+ String backupId,
+ String restoreToDatabaseId) {
+ BackupName backupName = BackupName.of(projectId, instanceId, backupId);
+ Backup backup = dbAdminClient.getBackup(backupName);
+ // Initiate the request which returns an OperationFuture.
+ System.out.println(String.format(
+ "Restoring backup [%s] to database [%s]...", backup.getName(), restoreToDatabaseId));
+ try {
+ RestoreDatabaseRequest request =
+ RestoreDatabaseRequest.newBuilder()
+ .setParent(InstanceName.of(projectId, instanceId).toString())
+ .setDatabaseId(restoreToDatabaseId)
+ .setBackup(backupName.toString()).build();
+ OperationFuture op =
+ dbAdminClient.restoreDatabaseAsync(request);
+ // Wait until the database has been restored.
+ com.google.spanner.admin.database.v1.Database db = op.get();
+ // Get the restore info.
+ RestoreInfo restoreInfo = db.getRestoreInfo();
+ BackupInfo backupInfo = restoreInfo.getBackupInfo();
+
+ System.out.println(
+ "Restored database ["
+ + db.getName()
+ + "] from ["
+ + restoreInfo.getBackupInfo().getBackup()
+ + "] with version time [" + backupInfo.getVersionTime() + "]");
+ } catch (ExecutionException e) {
+ throw SpannerExceptionFactory.newSpannerException(e.getCause());
+ } catch (InterruptedException e) {
+ throw SpannerExceptionFactory.propagateInterrupt(e);
+ }
+ }
+ // [END spanner_restore_backup]
+
+ // [START spanner_update_backup]
+ static void updateBackup(DatabaseAdminClient dbAdminClient, String projectId,
+ String instanceId, String backupId) {
+ BackupName backupName = BackupName.of(projectId, instanceId, backupId);
+
+ // Get current backup metadata.
+ Backup backup = dbAdminClient.getBackup(backupName);
+ // Add 30 days to the expire time.
+ // Expire time must be within 366 days of the create time of the backup.
+ Timestamp currentExpireTime = backup.getExpireTime();
+ com.google.cloud.Timestamp newExpireTime =
+ com.google.cloud.Timestamp.ofTimeMicroseconds(
+ TimeUnit.SECONDS.toMicros(currentExpireTime.getSeconds())
+ + TimeUnit.NANOSECONDS.toMicros(currentExpireTime.getNanos())
+ + TimeUnit.DAYS.toMicros(30L));
+
+ // New Expire Time must be less than Max Expire Time
+ newExpireTime =
+ newExpireTime.compareTo(com.google.cloud.Timestamp.fromProto(backup.getMaxExpireTime()))
+ < 0 ? newExpireTime : com.google.cloud.Timestamp.fromProto(backup.getMaxExpireTime());
+
+ System.out.println(String.format(
+ "Updating expire time of backup [%s] to %s...",
+ backupId.toString(),
+ java.time.OffsetDateTime.ofInstant(
+ Instant.ofEpochSecond(newExpireTime.getSeconds(),
+ newExpireTime.getNanos()), ZoneId.systemDefault())));
+
+ // Update expire time.
+ backup = backup.toBuilder().setExpireTime(newExpireTime.toProto()).build();
+ dbAdminClient.updateBackup(backup,
+ FieldMask.newBuilder().addAllPaths(Lists.newArrayList("expire_time")).build());
+ System.out.println("Updated backup [" + backupId + "]");
+ }
+ // [END spanner_update_backup]
+
+ // [START spanner_delete_backup]
+ static void deleteBackup(DatabaseAdminClient dbAdminClient,
+ String project, String instance, String backupId) {
+ BackupName backupName = BackupName.of(project, instance, backupId);
+
+ // Delete the backup.
+ System.out.println("Deleting backup [" + backupId + "]...");
+ dbAdminClient.deleteBackup(backupName);
+ // Verify that the backup is deleted.
+ try {
+ dbAdminClient.getBackup(backupName);
+ } catch (NotFoundException e) {
+ if (e.getStatusCode().getCode() == Code.NOT_FOUND) {
+ System.out.println("Deleted backup [" + backupId + "]");
+ } else {
+ System.out.println("Delete backup [" + backupId + "] failed");
+ throw new RuntimeException("Delete backup [" + backupId + "] failed", e);
+ }
+ }
+ }
+ // [END spanner_delete_backup]
+
+ static void run(
+ DatabaseClient dbClient,
+ DatabaseAdminClient dbAdminClient,
+ String command,
+ DatabaseId database,
+ String backupId) {
+ switch (command) {
+ case "createdatabase":
+ createDatabase(dbAdminClient, InstanceName.of(database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance()), database.getDatabase());
+ 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, DatabaseName.of(database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase()));
+ break;
+ case "update":
+ update(dbClient);
+ break;
+ case "writetransaction":
+ writeWithTransaction(dbClient);
+ break;
+ case "querymarketingbudget":
+ queryMarketingBudget(dbClient);
+ break;
+ case "addindex":
+ addIndex(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase()));
+ break;
+ case "readindex":
+ readUsingIndex(dbClient);
+ break;
+ case "queryindex":
+ queryUsingIndex(dbClient);
+ break;
+ case "addstoringindex":
+ addStoringIndex(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase()));
+ break;
+ case "readstoringindex":
+ readStoringIndex(dbClient);
+ break;
+ case "readonlytransaction":
+ readOnlyTransaction(dbClient);
+ break;
+ case "readstaledata":
+ readStaleData(dbClient);
+ break;
+ case "addcommittimestamp":
+ addCommitTimestamp(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase()));
+ break;
+ case "updatewithtimestamp":
+ updateWithTimestamp(dbClient);
+ break;
+ case "querywithtimestamp":
+ queryMarketingBudgetWithTimestamp(dbClient);
+ break;
+ case "createtablewithtimestamp":
+ createTableWithTimestamp(dbAdminClient,
+ DatabaseName.of(database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase()));
+ break;
+ case "writewithtimestamp":
+ writeExampleDataWithTimestamp(dbClient);
+ break;
+ case "querysingerstable":
+ querySingersTable(dbClient);
+ break;
+ case "queryperformancestable":
+ queryPerformancesTable(dbClient);
+ break;
+ case "writestructdata":
+ writeStructExampleData(dbClient);
+ break;
+ case "querywithstruct":
+ queryWithStruct(dbClient);
+ break;
+ case "querywitharrayofstruct":
+ queryWithArrayOfStruct(dbClient);
+ break;
+ case "querystructfield":
+ queryStructField(dbClient);
+ break;
+ case "querynestedstructfield":
+ queryNestedStructField(dbClient);
+ break;
+ case "insertusingdml":
+ insertUsingDml(dbClient);
+ break;
+ case "updateusingdml":
+ updateUsingDml(dbClient);
+ break;
+ case "deleteusingdml":
+ deleteUsingDml(dbClient);
+ break;
+ case "updateusingdmlwithtimestamp":
+ updateUsingDmlWithTimestamp(dbClient);
+ break;
+ case "writeandreadusingdml":
+ writeAndReadUsingDml(dbClient);
+ break;
+ case "updateusingdmlwithstruct":
+ updateUsingDmlWithStruct(dbClient);
+ break;
+ case "writeusingdml":
+ writeUsingDml(dbClient);
+ break;
+ case "querywithparameter":
+ queryWithParameter(dbClient);
+ break;
+ case "writewithtransactionusingdml":
+ writeWithTransactionUsingDml(dbClient);
+ break;
+ case "updateusingpartitioneddml":
+ updateUsingPartitionedDml(dbClient);
+ break;
+ case "deleteusingpartitioneddml":
+ deleteUsingPartitionedDml(dbClient);
+ break;
+ case "updateusingbatchdml":
+ updateUsingBatchDml(dbClient);
+ break;
+ case "createtablewithdatatypes":
+ createTableWithDatatypes(dbAdminClient,
+ DatabaseName.of(database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase()));
+ break;
+ case "writedatatypesdata":
+ writeDatatypesData(dbClient);
+ break;
+ case "querywitharray":
+ queryWithArray(dbClient);
+ break;
+ case "querywithbool":
+ queryWithBool(dbClient);
+ break;
+ case "querywithbytes":
+ queryWithBytes(dbClient);
+ break;
+ case "querywithdate":
+ queryWithDate(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 "createbackup":
+ createBackup(dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase(),
+ backupId, getVersionTime(dbClient));
+ break;
+ case "cancelcreatebackup":
+ cancelCreateBackup(
+ dbAdminClient,
+ database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase(),
+ backupId + "_cancel");
+ break;
+ case "listbackupoperations":
+ listBackupOperations(dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase(), backupId);
+ break;
+ case "listdatabaseoperations":
+ listDatabaseOperations(dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance());
+ break;
+ case "listbackups":
+ listBackups(dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), database.getDatabase(), backupId);
+ break;
+ case "restorebackup":
+ restoreBackup(
+ dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), backupId, database.getDatabase());
+ break;
+ case "updatebackup":
+ updateBackup(dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), backupId);
+ break;
+ case "deletebackup":
+ deleteBackup(dbAdminClient, database.getInstanceId().getProject(),
+ database.getInstanceId().getInstance(), backupId);
+ break;
+ default:
+ printUsageAndExit();
+ }
+ }
+
+ static Timestamp getVersionTime(DatabaseClient dbClient) {
+ // Generates a version time for the backup
+ com.google.cloud.Timestamp versionTime;
+ try (ResultSet resultSet = dbClient.singleUse()
+ .executeQuery(Statement.of("SELECT CURRENT_TIMESTAMP()"))) {
+ resultSet.next();
+ versionTime = resultSet.getTimestamp(0);
+ }
+ return versionTime.toProto();
+ }
+
+ static void printUsageAndExit() {
+ System.err.println("Usage:");
+ System.err.println(" SpannerExample ");
+ System.err.println("");
+ System.err.println("Examples:");
+ System.err.println(" SpannerExample createdatabase my-instance example-db");
+ System.err.println(" SpannerExample write my-instance example-db");
+ System.err.println(" SpannerExample delete my-instance example-db");
+ System.err.println(" SpannerExample query my-instance example-db");
+ System.err.println(" SpannerExample read my-instance example-db");
+ System.err.println(" SpannerExample addmarketingbudget my-instance example-db");
+ System.err.println(" SpannerExample update my-instance example-db");
+ System.err.println(" SpannerExample writetransaction my-instance example-db");
+ System.err.println(" SpannerExample querymarketingbudget my-instance example-db");
+ System.err.println(" SpannerExample addindex my-instance example-db");
+ System.err.println(" SpannerExample readindex my-instance example-db");
+ System.err.println(" SpannerExample queryindex my-instance example-db");
+ System.err.println(" SpannerExample addstoringindex my-instance example-db");
+ System.err.println(" SpannerExample readstoringindex my-instance example-db");
+ System.err.println(" SpannerExample readonlytransaction my-instance example-db");
+ System.err.println(" SpannerExample readstaledata my-instance example-db");
+ System.err.println(" SpannerExample addcommittimestamp my-instance example-db");
+ System.err.println(" SpannerExample updatewithtimestamp my-instance example-db");
+ System.err.println(" SpannerExample querywithtimestamp my-instance example-db");
+ System.err.println(" SpannerExample createtablewithtimestamp my-instance example-db");
+ System.err.println(" SpannerExample writewithtimestamp my-instance example-db");
+ System.err.println(" SpannerExample querysingerstable my-instance example-db");
+ System.err.println(" SpannerExample queryperformancestable my-instance example-db");
+ System.err.println(" SpannerExample writestructdata my-instance example-db");
+ System.err.println(" SpannerExample querywithstruct my-instance example-db");
+ System.err.println(" SpannerExample querywitharrayofstruct my-instance example-db");
+ System.err.println(" SpannerExample querystructfield my-instance example-db");
+ System.err.println(" SpannerExample querynestedstructfield my-instance example-db");
+ System.err.println(" SpannerExample insertusingdml my-instance example-db");
+ System.err.println(" SpannerExample updateusingdml my-instance example-db");
+ System.err.println(" SpannerExample deleteusingdml my-instance example-db");
+ System.err.println(" SpannerExample updateusingdmlwithtimestamp my-instance example-db");
+ System.err.println(" SpannerExample writeandreadusingdml my-instance example-db");
+ System.err.println(" SpannerExample updateusingdmlwithstruct my-instance example-db");
+ System.err.println(" SpannerExample writeusingdml my-instance example-db");
+ System.err.println(" SpannerExample querywithparameter my-instance example-db");
+ System.err.println(" SpannerExample writewithtransactionusingdml my-instance example-db");
+ System.err.println(" SpannerExample updateusingpartitioneddml my-instance example-db");
+ System.err.println(" SpannerExample deleteusingpartitioneddml my-instance example-db");
+ System.err.println(" SpannerExample updateusingbatchdml my-instance example-db");
+ System.err.println(" SpannerExample createtablewithdatatypes my-instance example-db");
+ System.err.println(" SpannerExample writedatatypesdata my-instance example-db");
+ System.err.println(" SpannerExample querywitharray my-instance example-db");
+ System.err.println(" SpannerExample querywithbool my-instance example-db");
+ System.err.println(" SpannerExample querywithbytes my-instance example-db");
+ System.err.println(" SpannerExample querywithdate my-instance example-db");
+ System.err.println(" SpannerExample querywithfloat my-instance example-db");
+ System.err.println(" SpannerExample querywithint my-instance example-db");
+ System.err.println(" SpannerExample querywithstring my-instance example-db");
+ System.err.println(" SpannerExample querywithtimestampparameter my-instance example-db");
+ System.err.println(" SpannerExample clientwithqueryoptions my-instance example-db");
+ 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 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 && args.length != 4) {
+ printUsageAndExit();
+ }
+ // [START init_client]
+ SpannerOptions options = SpannerOptions.newBuilder().build();
+ Spanner spanner = options.getService();
+ DatabaseAdminClient dbAdminClient = null;
+ try {
+ final String command = args[0];
+ DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]);
+ // [END init_client]
+ // 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();
+ }
+ // Generate a backup id for the sample database.
+ String backupId = null;
+ if (args.length == 4) {
+ backupId = args[3];
+ }
+
+ // [START init_client]
+ DatabaseClient dbClient = spanner.getDatabaseClient(db);
+ dbAdminClient = DatabaseAdminClient.create();
+
+ // Use client here...
+ // [END init_client]
+
+ run(dbClient, dbAdminClient, command, db, backupId);
+ // [START init_client]
+ } finally {
+ if (dbAdminClient != null) {
+ if (!dbAdminClient.isShutdown() || !dbAdminClient.isTerminated()) {
+ dbAdminClient.close();
+ }
+ }
+ spanner.close();
+ }
+ // [END init_client]
+ System.out.println("Closed client");
+ }
+}
diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CopyBackupIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/CopyBackupIT.java
new file mode 100644
index 0000000000..1d51042888
--- /dev/null
+++ b/samples/snippets/src/test/java/com/example/spanner/admin/generated/CopyBackupIT.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.spanner.admin.generated;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.example.spanner.SampleRunner;
+import com.google.cloud.spanner.ErrorCode;
+import com.google.cloud.spanner.SpannerException;
+import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Uninterruptibles;
+import com.google.spanner.admin.database.v1.BackupName;
+import com.google.spanner.admin.database.v1.DatabaseName;
+import com.google.spanner.admin.database.v1.InstanceName;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@Ignore
+public class CopyBackupIT extends SampleTestBaseV2 {
+
+ private static String key;
+
+ @BeforeClass
+ public static void setUp() {
+ String keyLocation = Preconditions
+ .checkNotNull(System.getProperty("spanner.test.key.location"));
+ String keyRing = Preconditions.checkNotNull(System.getProperty("spanner.test.key.ring"));
+ String keyName = Preconditions.checkNotNull(System.getProperty("spanner.test.key.name"));
+ key = "projects/" + projectId + "/locations/" + keyLocation + "/keyRings/" + keyRing
+ + "/cryptoKeys/" + keyName;
+ }
+
+ @Test
+ public void testEncryptedDatabaseAndBackupAndRestore() throws Exception {
+ final String databaseId = idGenerator.generateDatabaseId();
+ final String sourceBackupId = idGenerator.generateBackupId();
+ final String destinationBackupId = idGenerator.generateBackupId();
+
+ String out = SampleRunner.runSample(() ->
+ SpannerSample.createDatabase(
+ databaseAdminClient, InstanceName.of(projectId, instanceId), databaseId));
+ assertThat(out).contains(String.format(
+ "Created database [%s]", DatabaseName.of(projectId, instanceId, databaseId)));
+
+ out = SampleRunner.runSampleWithRetry(() ->
+ CreateBackupWithEncryptionKey.createBackupWithEncryptionKey(
+ databaseAdminClient, projectId, instanceId, databaseId, sourceBackupId, key
+ ), new ShouldRetryBackupOperation());
+ assertThat(out).containsMatch(
+ "Backup projects/" + projectId + "/instances/" + instanceId + "/backups/"
+ + sourceBackupId + " of size \\d+ bytes was created at (.*) using encryption key "
+ + key);
+
+ out = SampleRunner.runSampleWithRetry(() ->
+ CopyBackupSample.copyBackup(
+ databaseAdminClient, projectId, instanceId, sourceBackupId, destinationBackupId
+ ), new ShouldRetryBackupOperation());
+
+ assertThat(out).contains("Copied backup [" + BackupName.of(
+ projectId, instanceId, destinationBackupId).toString() + "]");
+ assertThat(out).containsMatch(String.format(
+ "Backup projects/%s/instances/%s/backups/%s of size \\d+ bytes was copied at (.*)",
+ projectId, instanceId, destinationBackupId, key));
+ }
+
+ static class ShouldRetryBackupOperation implements Predicate {
+
+ private static final int MAX_ATTEMPTS = 20;
+ private int attempts = 0;
+
+ @Override
+ public boolean test(SpannerException e) {
+ if (e.getErrorCode() == ErrorCode.FAILED_PRECONDITION
+ && e.getMessage().contains("Please retry the operation once the pending")) {
+ attempts++;
+ if (attempts == MAX_ATTEMPTS) {
+ // Throw custom exception so it is easier to locate in the log why it went wrong.
+ throw SpannerExceptionFactory.newSpannerException(ErrorCode.DEADLINE_EXCEEDED,
+ String.format("Operation failed %d times because of other pending operations. "
+ + "Giving up operation.\n", attempts),
+ e);
+ }
+ // Wait one minute before retrying.
+ Uninterruptibles.sleepUninterruptibly(60L, TimeUnit.SECONDS);
+ return true;
+ }
+ return false;
+ }
+ }
+}
+
diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/EncryptionKeyIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/EncryptionKeyIT.java
new file mode 100644
index 0000000000..5b2513d91c
--- /dev/null
+++ b/samples/snippets/src/test/java/com/example/spanner/admin/generated/EncryptionKeyIT.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.spanner.admin.generated;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.example.spanner.SampleRunner;
+import com.google.cloud.spanner.ErrorCode;
+import com.google.cloud.spanner.SpannerException;
+import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Uninterruptibles;
+import com.google.spanner.admin.database.v1.DatabaseName;
+import com.google.spanner.admin.database.v1.InstanceName;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Integration tests for: {@link com.example.spanner.CreateDatabaseWithEncryptionKey},
+ * {@link com.example.spanner.CreateBackupWithEncryptionKey} and
+ * {@link com.example.spanner.RestoreBackupWithEncryptionKey}
+ */
+@RunWith(JUnit4.class)
+@Ignore
+public class EncryptionKeyIT extends SampleTestBaseV2 {
+
+ private static String key;
+
+ @BeforeClass
+ public static void setUp() {
+ String keyLocation = Preconditions
+ .checkNotNull(System.getProperty("spanner.test.key.location"));
+ String keyRing = Preconditions.checkNotNull(System.getProperty("spanner.test.key.ring"));
+ String keyName = Preconditions.checkNotNull(System.getProperty("spanner.test.key.name"));
+ key = "projects/" + projectId + "/locations/" + keyLocation + "/keyRings/" + keyRing
+ + "/cryptoKeys/" + keyName;
+ }
+
+ @Test
+ public void testEncryptedDatabaseAndBackupAndRestore() throws Exception {
+ final String databaseId = idGenerator.generateDatabaseId();
+ final String backupId = idGenerator.generateBackupId();
+ final String restoreId = idGenerator.generateDatabaseId();
+
+ String out = SampleRunner.runSample(() ->
+ SpannerSample.createDatabase(
+ databaseAdminClient, InstanceName.of(projectId, instanceId), databaseId));
+ assertThat(out).contains(String.format(
+ "Created database [%s]", DatabaseName.of(projectId, instanceId, databaseId)));
+
+ out = SampleRunner.runSampleWithRetry(() ->
+ CreateBackupWithEncryptionKey.createBackupWithEncryptionKey(
+ databaseAdminClient,
+ projectId,
+ instanceId,
+ databaseId,
+ backupId,
+ key
+ ), new ShouldRetryBackupOperation());
+ assertThat(out).containsMatch(
+ "Backup projects/" + projectId + "/instances/" + instanceId + "/backups/" + backupId
+ + " of size \\d+ bytes was created at (.*) using encryption key " + key);
+
+ out = SampleRunner.runSampleWithRetry(() ->
+ RestoreBackupWithEncryptionKey.restoreBackupWithEncryptionKey(
+ databaseAdminClient,
+ projectId,
+ instanceId,
+ backupId,
+ restoreId,
+ key
+ ), new ShouldRetryBackupOperation());
+ assertThat(out).contains(
+ "Database projects/" + projectId + "/instances/" + instanceId + "/databases/" + databaseId
+ + " restored to projects/" + projectId + "/instances/" + instanceId + "/databases/"
+ + restoreId + " from backup projects/" + projectId + "/instances/" + instanceId
+ + "/backups/" + backupId + " using encryption key " + key);
+ }
+
+ static class ShouldRetryBackupOperation implements Predicate {
+
+ private static final int MAX_ATTEMPTS = 20;
+ private int attempts = 0;
+
+ @Override
+ public boolean test(SpannerException e) {
+ if (e.getErrorCode() == ErrorCode.FAILED_PRECONDITION
+ && e.getMessage().contains("Please retry the operation once the pending")) {
+ attempts++;
+ if (attempts == MAX_ATTEMPTS) {
+ // Throw custom exception so it is easier to locate in the log why it went wrong.
+ throw SpannerExceptionFactory.newSpannerException(ErrorCode.DEADLINE_EXCEEDED,
+ String.format("Operation failed %d times because of other pending operations. "
+ + "Giving up operation.\n", attempts),
+ e);
+ }
+ // Wait one minute before retrying.
+ Uninterruptibles.sleepUninterruptibly(60L, TimeUnit.SECONDS);
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/PgSpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/PgSpannerSampleIT.java
new file mode 100644
index 0000000000..90885b71ce
--- /dev/null
+++ b/samples/snippets/src/test/java/com/example/spanner/admin/generated/PgSpannerSampleIT.java
@@ -0,0 +1,289 @@
+/*
+ * 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.admin.generated;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.cloud.Timestamp;
+import com.google.cloud.spanner.DatabaseId;
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
+import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.spanner.admin.database.v1.Database;
+import com.google.spanner.admin.database.v1.DatabaseName;
+import com.google.spanner.admin.database.v1.InstanceName;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+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 extends SampleTestBaseV2 {
+
+ 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");
+ static Spanner spanner;
+ static DatabaseId dbId;
+ static DatabaseAdminClient dbClient;
+
+ @BeforeClass
+ public static void setUp() throws IOException {
+ SpannerOptions options =
+ SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build();
+ spanner = options.getService();
+ dbClient = DatabaseAdminClient.create();
+ dbId = DatabaseId.of(options.getProjectId(), instanceId, idGenerator.generateDatabaseId());
+ // 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(
+ InstanceName.of(projectId, instanceId)).iterateAll()) {
+ DatabaseName databaseName = DatabaseName.parse(db.getName());
+ if (TimeUnit.HOURS.convert(now.getSeconds() - db.getCreateTime().getSeconds(),
+ TimeUnit.SECONDS) > 24) {
+ if (databaseName.getDatabase().length() >= DBID_LENGTH) {
+ if (samplePattern.matcher(
+ toComparableId(PgSpannerSampleIT.baseDbId, databaseName.getDatabase())).matches()) {
+ dbClient.dropDatabase(db.getName());
+ }
+ if (restoredPattern.matcher(toComparableId("restored", databaseName.getDatabase()))
+ .matches()) {
+ dbClient.dropDatabase(db.getName());
+ }
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+
+ private String runSample(String command) throws Exception {
+ final PrintStream stdOut = System.out;
+ final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ final PrintStream out = new PrintStream(bout);
+ System.setOut(out);
+ System.out.println(instanceId + ":" + dbId.getDatabase());
+ PgSpannerSample.main(new String[]{command, instanceId, dbId.getDatabase()});
+ System.setOut(stdOut);
+ return bout.toString();
+ }
+
+ @Test
+ public void testSample() throws Exception {
+ assertThat(instanceId).isNotNull();
+ assertThat(dbId.getDatabase()).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/admin/generated/SpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java
new file mode 100644
index 0000000000..cf719ff9e4
--- /dev/null
+++ b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java
@@ -0,0 +1,694 @@
+/*
+ * Copyright 2017 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.admin.generated;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.example.spanner.SampleRunner;
+import com.google.cloud.Timestamp;
+import com.google.cloud.spanner.DatabaseId;
+import com.google.cloud.spanner.ErrorCode;
+import com.google.cloud.spanner.Instance;
+import com.google.cloud.spanner.InstanceAdminClient;
+import com.google.cloud.spanner.InstanceConfigId;
+import com.google.cloud.spanner.InstanceId;
+import com.google.cloud.spanner.InstanceInfo;
+import com.google.cloud.spanner.Options;
+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.cloud.spanner.admin.database.v1.DatabaseAdminClient;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Uninterruptibles;
+import com.google.spanner.admin.database.v1.Backup;
+import com.google.spanner.admin.database.v1.BackupName;
+import com.google.spanner.admin.database.v1.Database;
+import com.google.spanner.admin.database.v1.DatabaseName;
+import com.google.spanner.admin.database.v1.InstanceName;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Unit tests for {@code SpannerSample}
+ */
+@RunWith(JUnit4.class)
+@SuppressWarnings("checkstyle:abbreviationaswordinname")
+public class SpannerSampleIT extends SampleTestBaseV2 {
+
+ 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 keyLocation =
+ Preconditions.checkNotNull(System.getProperty("spanner.test.key.location"));
+ private static final String keyRing =
+ Preconditions.checkNotNull(System.getProperty("spanner.test.key.ring"));
+ private static final String keyName =
+ Preconditions.checkNotNull(System.getProperty("spanner.test.key.name"));
+ private static final String encryptedBackupId = formatForTest(baseDbId);
+ private static final long STALE_INSTANCE_THRESHOLD_SECS =
+ TimeUnit.SECONDS.convert(24L, TimeUnit.HOURS);
+ static Spanner spanner;
+ static DatabaseAdminClient databaseAdminClient;
+ private static String key;
+ private long lastUpdateDataTimeInMillis;
+
+ private String runSample(String command, String databaseId) throws Exception {
+ PrintStream stdOut = System.out;
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(bout);
+ System.setOut(out);
+ SpannerSample.main(new String[]{command, instanceId, databaseId, null});
+ System.setOut(stdOut);
+ return bout.toString();
+ }
+
+ private String runSample(String command, String databaseId, String backupId) throws Exception {
+ PrintStream stdOut = System.out;
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(bout);
+ System.setOut(out);
+ SpannerSample.main(new String[]{command, instanceId, databaseId, backupId});
+ System.setOut(stdOut);
+ return bout.toString();
+ }
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ SpannerOptions options =
+ SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build();
+ spanner = options.getService();
+ databaseAdminClient = DatabaseAdminClient.create();
+ // Delete stale test databases that have been created earlier by this test, but not deleted.
+ deleteStaleTestDatabases();
+ key =
+ String.format(
+ "projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s",
+ options.getProjectId(), keyLocation, keyRing, keyName);
+
+ /*
+ * Delete stale instances that have been created earlier by this test but not deleted.
+ * Backups needed to be deleted from the instance first, as the instance can only be
+ * deleted once all backups have been deleted.
+ * */
+ deleteStaleEncryptedTestInstances();
+ }
+
+ /**
+ * Deleting all the test instances with name starting with 'encrypted-test-' and were created
+ * before 24 hours.
+ *
+ * @throws InterruptedException If Thread.sleep() interrupted
+ */
+ private static void deleteStaleEncryptedTestInstances() throws InterruptedException {
+ Timestamp now = Timestamp.now();
+
+ for (Instance instance :
+ spanner
+ .getInstanceAdminClient()
+ .listInstances(Options.filter("name:encrypted-test-"))
+ .iterateAll()) {
+ if ((now.getSeconds() - instance.getCreateTime().getSeconds())
+ > STALE_INSTANCE_THRESHOLD_SECS) {
+ deleteAllBackups(instance.getId().getInstance());
+ instance.delete();
+ }
+ }
+ }
+
+ static void deleteStaleTestDatabases() throws IOException {
+ Timestamp now = Timestamp.now();
+ Pattern samplePattern = getTestDbIdPattern(SpannerSampleIT.baseDbId);
+ Pattern restoredPattern = getTestDbIdPattern("restored");
+ try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
+ for (Database db : databaseAdminClient.listDatabases(InstanceName.of(projectId, instanceId))
+ .iterateAll()) {
+ DatabaseName databaseName = DatabaseName.parse(db.getName());
+ if (TimeUnit.HOURS.convert(now.getSeconds() - db.getCreateTime().getSeconds(),
+ TimeUnit.SECONDS) > 24) {
+ if (databaseName.getDatabase().length() >= DBID_LENGTH) {
+ if (samplePattern.matcher(
+ toComparableId(SpannerSampleIT.baseDbId, databaseName.getDatabase())).matches()) {
+ databaseAdminClient.dropDatabase(db.getName());
+ }
+ if (restoredPattern.matcher(toComparableId("restored", databaseName.getDatabase()))
+ .matches()) {
+ databaseAdminClient.dropDatabase(db.getName());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ databaseAdminClient.deleteBackup(BackupName.of(projectId, instanceId, encryptedBackupId));
+ spanner.close();
+ }
+
+ @Test
+ public void testSample() throws Exception {
+ String databaseId = idGenerator.generateDatabaseId();
+ DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId);
+ assertThat(instanceId).isNotNull();
+ assertThat(databaseId).isNotNull();
+ String out = runSample("createdatabase", databaseId);
+ assertThat(out).contains("Created database");
+ assertThat(out).contains(dbId.getName());
+
+ System.out.println("Write data to sample tables ...");
+ runSample("write", databaseId);
+
+ System.out.println("Delete data to sample tables ...");
+ out = runSample("delete", databaseId);
+ assertThat(out).contains("Records deleted.");
+
+ runSample("write", databaseId);
+
+ System.out.println("Read data from sample tables ...");
+ out = runSample("read", databaseId);
+ assertThat(out).contains("1 1 Total Junk");
+
+ out = runSample("query", databaseId);
+ assertThat(out).contains("1 1 Total Junk");
+ runSample("addmarketingbudget", databaseId);
+
+ // wait for 15 seconds to elapse and then run an update, and query for stale data
+ lastUpdateDataTimeInMillis = System.currentTimeMillis();
+ while (System.currentTimeMillis() < lastUpdateDataTimeInMillis + 16000) {
+ Thread.sleep(1000);
+ }
+ runSample("update", databaseId);
+
+ System.out.println("Read stale data from sample tables ...");
+ out = runSample("readstaledata", databaseId);
+ assertThat(out).contains("1 1 NULL");
+ runSample("writetransaction", databaseId);
+
+ System.out.println("Query marketing budget ...");
+ out = runSample("querymarketingbudget", databaseId);
+ assertThat(out).contains("1 1 300000");
+ assertThat(out).contains("2 2 300000");
+
+ System.out.println("Add index ...");
+ runSample("addindex", databaseId);
+
+ System.out.println("Query index ...");
+ out = runSample("queryindex", databaseId);
+ assertThat(out).contains("Go, Go, Go");
+ assertThat(out).contains("Forever Hold Your Peace");
+ assertThat(out).doesNotContain("Green");
+
+ System.out.println("Read index ...");
+ out = runSample("readindex", databaseId);
+ 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", databaseId);
+ out = runSample("readstoringindex", databaseId);
+ assertThat(out).contains("300000");
+
+ System.out.println("Read storing index ...");
+ out = runSample("readonlytransaction", databaseId);
+ assertThat(out.replaceAll("[\r\n]+", " ")).containsMatch("(Total Junk.*){2}");
+
+ out = runSample("addcommittimestamp", databaseId);
+ assertThat(out).contains("Added LastUpdateTime as a commit timestamp column");
+
+ runSample("updatewithtimestamp", databaseId);
+ out = runSample("querywithtimestamp", databaseId);
+ assertThat(out).contains("1 1 1000000");
+ assertThat(out).contains("2 2 750000");
+
+ out = runSample("createtablewithtimestamp", databaseId);
+ assertThat(out).contains("Created Performances table in database");
+
+ runSample("writewithtimestamp", databaseId);
+ out = runSample("queryperformancestable", databaseId);
+ assertThat(out).contains("1 4 2017-10-05 11000");
+ assertThat(out).contains("1 19 2017-11-02 15000");
+ assertThat(out).contains("2 42 2017-12-23 7000");
+
+ runSample("writestructdata", databaseId);
+ out = runSample("querywithstruct", databaseId);
+ assertThat(out).startsWith("6\n");
+
+ out = runSample("querywitharrayofstruct", databaseId);
+ assertThat(out).startsWith("8\n7\n6");
+
+ out = runSample("querystructfield", databaseId);
+ assertThat(out).startsWith("6\n");
+
+ out = runSample("querynestedstructfield", databaseId);
+ assertThat(out).contains("6 Imagination\n");
+ assertThat(out).contains("9 Imagination\n");
+
+ runSample("insertusingdml", databaseId);
+ out = runSample("querysingerstable", databaseId);
+ assertThat(out).contains("Virginia Watson");
+
+ runSample("updateusingdml", databaseId);
+ out = runSample("querymarketingbudget", databaseId);
+ assertThat(out).contains("1 1 2000000");
+
+ runSample("deleteusingdml", databaseId);
+ out = runSample("querysingerstable", databaseId);
+ assertThat(out).doesNotContain("Alice Trentor");
+
+ out = runSample("updateusingdmlwithtimestamp", databaseId);
+ assertThat(out).contains("2 records updated");
+
+ out = runSample("writeandreadusingdml", databaseId);
+ assertThat(out).contains("Timothy Campbell");
+
+ runSample("updateusingdmlwithstruct", databaseId);
+ out = runSample("querysingerstable", databaseId);
+ assertThat(out).contains("Timothy Grant");
+
+ runSample("writeusingdml", databaseId);
+ out = runSample("querysingerstable", databaseId);
+ assertThat(out).contains("Melissa Garcia");
+ assertThat(out).contains("Russell Morales");
+ assertThat(out).contains("Jacqueline Long");
+ assertThat(out).contains("Dylan Shaw");
+ out = runSample("querywithparameter", databaseId);
+ assertThat(out).contains("12 Melissa Garcia");
+
+ runSample("writewithtransactionusingdml", databaseId);
+ out = runSample("querymarketingbudget", databaseId);
+ assertThat(out).contains("1 1 2200000");
+ assertThat(out).contains("2 2 550000");
+
+ runSample("updateusingpartitioneddml", databaseId);
+ out = runSample("querymarketingbudget", databaseId);
+ assertThat(out).contains("1 1 2200000");
+ assertThat(out).contains("2 2 100000");
+
+ runSample("deleteusingpartitioneddml", databaseId);
+ out = runSample("querysingerstable", databaseId);
+ 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");
+
+ out = runSample("updateusingbatchdml", databaseId);
+ assertThat(out).contains("1 record updated by stmt 0");
+ assertThat(out).contains("1 record updated by stmt 1");
+
+ out = runSample("createtablewithdatatypes", databaseId);
+ assertThat(out).contains("Created Venues table in database");
+
+ runSample("writedatatypesdata", databaseId);
+ out = runSample("querywitharray", databaseId);
+ assertThat(out).contains("19 Venue 19 2020-11-01");
+ assertThat(out).contains("42 Venue 42 2020-10-01");
+
+ out = runSample("querywithbool", databaseId);
+ assertThat(out).contains("19 Venue 19 true");
+
+ out = runSample("querywithbytes", databaseId);
+ assertThat(out).contains("4 Venue 4");
+
+ out = runSample("querywithdate", databaseId);
+ assertThat(out).contains("4 Venue 4 2018-09-02");
+ assertThat(out).contains("42 Venue 42 2018-10-01");
+
+ out = runSample("querywithfloat", databaseId);
+ assertThat(out).contains("4 Venue 4 0.8");
+ assertThat(out).contains("19 Venue 19 0.9");
+
+ out = runSample("querywithint", databaseId);
+ assertThat(out).contains("19 Venue 19 6300");
+ assertThat(out).contains("42 Venue 42 3000");
+
+ out = runSample("querywithstring", databaseId);
+ assertThat(out).contains("42 Venue 42");
+
+ out = runSample("querywithtimestampparameter", databaseId);
+ assertThat(out).contains("4 Venue 4");
+ assertThat(out).contains("19 Venue 19");
+ assertThat(out).contains("42 Venue 42");
+
+ out = runSample("querywithnumeric", databaseId);
+ assertThat(out).contains("19 Venue 19 1200100");
+ assertThat(out).contains("42 Venue 42 390650.99");
+
+ out = runSample("clientwithqueryoptions", databaseId);
+ assertThat(out).contains("1 1 Total Junk");
+ out = runSample("querywithqueryoptions", databaseId);
+ assertThat(out).contains("1 1 Total Junk");
+ }
+
+ @Test
+ public void testBackupSamples_withoutEncryption() {
+ String databaseId = idGenerator.generateDatabaseId();
+ DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId);
+ String restoreDatabaseId = idGenerator.generateDatabaseId();
+ String backupId = idGenerator.generateBackupId();
+
+ try {
+ assertThat(instanceId).isNotNull();
+ assertThat(databaseId).isNotNull();
+
+ System.out.println("Creating Database ...");
+ String out = runSample("createdatabase", databaseId);
+ assertThat(out).contains("Created database");
+ assertThat(out).contains(dbId.getName());
+
+ BackupName backupName = BackupName.of(projectId, instanceId, backupId);
+
+ System.out.println("Creating Backup ...");
+ out = runSample("createbackup", databaseId, backupId);
+ assertThat(out).contains("Created backup [" + backupName.toString() + "]");
+
+ // TODO: remove try-catch when filtering on metadata fields works.
+ try {
+ System.out.println("List Backup Operations ...");
+ out = runSample("listbackupoperations", databaseId, backupId);
+ assertThat(out).contains(
+ String.format(
+ "Backup %s on database %s pending:", backupName, 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");
+ }
+
+ System.out.println("List Backup ...");
+ out = runSample("listbackups", databaseId, backupId);
+ assertThat(out).contains("All backups:");
+ assertThat(out).contains(
+ String.format("All backups with backup name containing \"%s\":", backupId));
+ assertThat(out).contains(String.format(
+ "All backups for databases with a name containing \"%s\":",
+ dbId.getDatabase()));
+ assertThat(out).contains(
+ String.format("All backups that expire before"));
+ assertThat(out).contains("All backups with size greater than 100 bytes:");
+ assertThat(out).containsMatch(
+ Pattern.compile("All databases created after (.+) and that are ready:"));
+ assertThat(out).contains("All backups, listed using pagination:");
+ // All the above tests should include the created backup exactly once, i.e. exactly 6 times.
+ assertThat(countOccurrences(out, backupName.toString())).isEqualTo(6);
+
+ // Try the restore operation in a retry loop, as there is a limit on the number of restore
+ // operations that is allowed to execute simultaneously, and we should retry if we hit this
+ // limit.
+ boolean restored = false;
+ int restoreAttempts = 0;
+ while (true) {
+ try {
+ System.out.println("Restore Backup ...");
+ out = runSample("restorebackup", restoreDatabaseId, backupId);
+ assertThat(out).contains(
+ "Restored database ["
+ + DatabaseName.of(projectId, instanceId, restoreDatabaseId).toString()
+ + "] from ["
+ + backupName
+ + "]");
+ restored = true;
+ break;
+ } catch (SpannerException e) {
+ if (e.getErrorCode() == ErrorCode.FAILED_PRECONDITION
+ && e.getMessage()
+ .contains("Please retry the operation once the pending restores complete")) {
+ restoreAttempts++;
+ if (restoreAttempts == 10) {
+ System.out.println(
+ "Restore operation failed 10 times because of other pending restores. "
+ + "Giving up restore.");
+ break;
+ }
+ Uninterruptibles.sleepUninterruptibly(60L, TimeUnit.SECONDS);
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ if (restored) {
+ System.out.println("List Database Operations ...");
+ out = runSample("listdatabaseoperations", restoreDatabaseId);
+ assertThat(out).contains(
+ String.format(
+ "Database %s restored from backup",
+ DatabaseId.of(dbId.getInstanceId(), restoreDatabaseId).getName()));
+ }
+
+ System.out.println("Updating backup ...");
+ out = runSample("updatebackup", databaseId, backupId);
+ assertThat(out).contains(
+ String.format("Updated backup [" + backupId + "]"));
+
+ // Drop the restored database before we try to delete the backup.
+ // Otherwise the delete backup operation might fail as the backup is still in use by
+ // the OptimizeRestoredDatabase operation.
+ databaseAdminClient.dropDatabase(DatabaseName.of(projectId,
+ dbId.getInstanceId().getInstance(), restoreDatabaseId));
+
+ System.out.println("Deleting Backup ...");
+ out = runSample("deletebackup", databaseId, backupId);
+ assertThat(out).contains("Deleted backup [" + backupId + "]");
+
+ } catch (Exception ex) {
+ Assert.fail("Exception raised => " + ex.getCause());
+ }
+ }
+
+ @Test
+ public void testCancelBackupSamples() {
+ String databaseId = idGenerator.generateDatabaseId();
+ DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId);
+
+ try {
+ assertThat(instanceId).isNotNull();
+ assertThat(databaseId).isNotNull();
+
+ String out = runSample("createdatabase", databaseId);
+ assertThat(out).contains("Created database");
+ assertThat(out).contains(dbId.getName());
+
+ String backupId = idGenerator.generateBackupId();
+
+ out = runSample("cancelcreatebackup", databaseId, backupId);
+ assertThat(out).contains(
+ "Backup operation for [" + backupId + "_cancel] successfully");
+ } catch (Exception ex) {
+ Assert.fail("Exception raised => " + ex.getCause());
+ }
+ }
+
+ @Test
+ public void testEncryptedDatabaseAndBackupSamples() throws Exception {
+ String projectId = spanner.getOptions().getProjectId();
+ String databaseId = idGenerator.generateDatabaseId();
+ String restoreId = idGenerator.generateDatabaseId();
+ // Create a separate instance for this test to prevent multiple parallel backup operations on
+ // the same instance that need to wait for each other.
+ String instanceId = idGenerator.generateInstanceId();
+ InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient();
+ instanceAdminClient
+ .createInstance(InstanceInfo.newBuilder(InstanceId.of(projectId, instanceId))
+ .setDisplayName("Encrypted test instance")
+ .setInstanceConfigId(InstanceConfigId.of(projectId, "regional-" + keyLocation))
+ .setNodeCount(1).build())
+ .get();
+ try {
+ String out = SampleRunner
+ .runSample(() -> SpannerSample.createDatabase(
+ databaseAdminClient, InstanceName.of(projectId, instanceId), databaseId));
+ assertThat(out).contains(String.format(
+ "Created database [%s]", DatabaseName.of(projectId, instanceId, databaseId)));
+
+ out = SampleRunner.runSampleWithRetry(
+ () -> CreateBackupWithEncryptionKey.createBackupWithEncryptionKey(databaseAdminClient,
+ projectId,
+ instanceId, databaseId, encryptedBackupId, key),
+ new ShouldRetryBackupOperation());
+ assertThat(out).containsMatch(String.format(
+ "Backup projects/%s/instances/%s/backups/%s of size \\d+ bytes "
+ + "was created at (.*) using encryption key %s",
+ projectId, instanceId, encryptedBackupId, key));
+
+ out = SampleRunner.runSampleWithRetry(
+ () -> RestoreBackupWithEncryptionKey.restoreBackupWithEncryptionKey(databaseAdminClient,
+ projectId, instanceId, encryptedBackupId, restoreId, key),
+ new ShouldRetryBackupOperation());
+ assertThat(out).contains(String.format(
+ "Database projects/%s/instances/%s/databases/%s"
+ + " restored to projects/%s/instances/%s/databases/%s"
+ + " from backup projects/%s/instances/%s/backups/%s" + " using encryption key %s",
+ projectId, instanceId, databaseId, projectId, instanceId, restoreId,
+ projectId, instanceId, encryptedBackupId, key));
+ } finally {
+ // Delete the backups from the test instance first, as the instance can only be deleted once
+ // all backups have been deleted.
+ deleteAllBackups(instanceId);
+ instanceAdminClient.deleteInstance(instanceId);
+ }
+ }
+
+ @Test
+ public void testDeleteBackups() {
+ try {
+ String projectId = spanner.getOptions().getProjectId();
+ String databaseId = idGenerator.generateDatabaseId();
+ String backupId = idGenerator.generateBackupId();
+
+ String out = SampleRunner
+ .runSample(() -> SpannerSample.createDatabase(
+ databaseAdminClient, InstanceName.of(projectId, instanceId), databaseId));
+ assertThat(out).contains(String.format(
+ "Created database [%s]", DatabaseName.of(projectId, instanceId, databaseId)));
+
+ out = SampleRunner.runSampleWithRetry(
+ () -> CreateBackupWithEncryptionKey.createBackupWithEncryptionKey(databaseAdminClient,
+ projectId, instanceId, databaseId, backupId, key),
+ new ShouldRetryBackupOperation());
+ assertThat(out).containsMatch(String.format(
+ "Backup projects/%s/instances/%s/backups/%s of size \\d+ bytes "
+ + "was created at (.*) using encryption key %s",
+ projectId, instanceId, backupId, key));
+
+ out = runSample("deletebackup", databaseId, backupId);
+ assertThat(out).contains("Deleted backup [" + backupId + "]");
+ } catch (Exception ex) {
+ Assert.fail("Exception raised => " + ex.getCause());
+ }
+ }
+
+ private static void deleteAllBackups(String instanceId) throws InterruptedException {
+ InstanceName instanceName = InstanceName.of(projectId, instanceId);
+ for (Backup backup : databaseAdminClient.listBackups(instanceName.toString()).iterateAll()) {
+ int attempts = 0;
+ while (attempts < 30) {
+ try {
+ attempts++;
+ databaseAdminClient.deleteBackup(backup.getName());
+ break;
+ } catch (SpannerException e) {
+ if (e.getErrorCode() == ErrorCode.FAILED_PRECONDITION && e.getMessage()
+ .contains("Please try deleting the backup once the restore or post-restore optimize "
+ + "operations have completed on these databases.")) {
+ // Wait 30 seconds and then retry.
+ Thread.sleep(30_000L);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+ }
+
+ private String runSampleRunnable(Runnable sample) {
+ PrintStream stdOut = System.out;
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(bout);
+ System.setOut(out);
+ sample.run();
+ System.setOut(stdOut);
+ return bout.toString();
+ }
+
+ @Test
+ public void testCreateInstanceSample() {
+ String databaseId = idGenerator.generateDatabaseId();
+ DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId);
+
+ String instanceId = formatForTest("sample-inst");
+ String out =
+ runSampleRunnable(() -> {
+ try {
+ CreateInstanceExample.createInstance(
+ dbId.getInstanceId().getProject(), instanceId);
+ } catch (IOException ex) {
+ System.out.println("Got exception => " + ex);
+ } finally {
+ spanner.getInstanceAdminClient().deleteInstance(instanceId);
+ }
+ });
+ assertThat(out)
+ .contains(
+ String.format(
+ "Instance %s was successfully created",
+ InstanceId.of(dbId.getInstanceId().getProject(), instanceId)));
+ }
+
+ private static int countOccurrences(String input, String search) {
+ return input.split(search).length - 1;
+ }
+
+ 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);
+ }
+
+ static class ShouldRetryBackupOperation implements Predicate {
+
+ private static final int MAX_ATTEMPTS = 20;
+ private int attempts = 0;
+
+ @Override
+ public boolean test(SpannerException e) {
+ if (e.getErrorCode() == ErrorCode.FAILED_PRECONDITION
+ && e.getMessage().contains("Please retry the operation once the pending")) {
+ attempts++;
+ if (attempts == MAX_ATTEMPTS) {
+ // Throw custom exception so it is easier to locate in the log why it went wrong.
+ throw SpannerExceptionFactory.newSpannerException(ErrorCode.DEADLINE_EXCEEDED,
+ String.format("Operation failed %d times because of other pending operations. "
+ + "Giving up operation.\n", attempts),
+ e);
+ }
+ // Wait one minute before retrying.
+ Uninterruptibles.sleepUninterruptibly(60L, TimeUnit.SECONDS);
+ return true;
+ }
+ return false;
+ }
+ }
+}
From cd34c1d3ae9a5a36f4d5516dcf7c3667a9cf015a Mon Sep 17 00:00:00 2001
From: Arpan Mishra
Date: Fri, 23 Feb 2024 19:31:33 +0530
Subject: [PATCH 04/16] fix: flaky test issue due to AbortedException. (#2925)
* fix: prevent illegal negative timeout values into thread sleep() method while retrying exceptions in unit tests.
* For details on issue see - https://github.com/googleapis/java-spanner/issues/2206
* Fixing lint issues.
* fix: flaky test issue due to AbortedException.
* chore: remove assertion.
---
.../java/com/google/cloud/spanner/it/ITTransactionTest.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java
index 2cfb9cbbc5..ea60b9fb64 100644
--- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java
+++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java
@@ -298,7 +298,6 @@ public void readAbort() throws Exception {
} catch (SpannerException e) {
if (e.getErrorCode() == ErrorCode.ABORTED) {
assertThat(e).isInstanceOf(AbortedException.class);
- assertThat(e.getRetryDelayInMillis()).isNotEqualTo(-1L);
}
throw new RuntimeException("Swallowed exception: " + e.getMessage());
}
@@ -338,7 +337,6 @@ public void readAbort() throws Exception {
} catch (SpannerException e) {
if (e.getErrorCode() == ErrorCode.ABORTED) {
assertThat(e).isInstanceOf(AbortedException.class);
- assertThat(e.getRetryDelayInMillis()).isNotEqualTo(-1L);
}
throw new RuntimeException("Swallowed exception: " + e.getMessage());
}
From c6d82d4bd3a27490ed253a178d03d3e534bf0653 Mon Sep 17 00:00:00 2001
From: Arpan Mishra
Date: Tue, 27 Feb 2024 00:25:33 +0530
Subject: [PATCH 05/16] chore: replace client creation methods for all new
samples with auto-generated admin clients (#2927)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* fix: prevent illegal negative timeout values into thread sleep() method while retrying exceptions in unit tests.
* For details on issue see - https://github.com/googleapis/java-spanner/issues/2206
* Fixing lint issues.
* chore: remove unused imports.
* chore: replace with new client creation methods.
* Update samples/snippets/src/main/java/com/example/spanner/admin/generated/AddAndDropDatabaseRole.java
Co-authored-by: Knut Olav Løite
* chore: address comments.
* chore: replace with new client creation methods.
---------
Co-authored-by: Knut Olav Løite
---
.../generated/AddAndDropDatabaseRole.java | 17 ++-
.../admin/generated/AddJsonColumnSample.java | 29 +++--
.../admin/generated/AddJsonbColumnSample.java | 31 +++--
.../generated/AddNumericColumnSample.java | 28 +++--
.../admin/generated/AlterSequenceSample.java | 10 +-
...ableWithForeignKeyDeleteCascadeSample.java | 39 ++++---
.../admin/generated/CopyBackupSample.java | 9 +-
.../CreateBackupWithEncryptionKey.java | 11 +-
.../CreateDatabaseWithEncryptionKey.java | 9 +-
...abaseWithVersionRetentionPeriodSample.java | 13 ++-
.../generated/CreateInstanceConfigSample.java | 14 ++-
.../generated/CreateInstanceExample.java | 18 ++-
...eInstanceWithAutoscalingConfigExample.java | 14 ++-
...ateInstanceWithProcessingUnitsExample.java | 15 ++-
.../admin/generated/CreateSequenceSample.java | 11 +-
...ableWithForeignKeyDeleteCascadeSample.java | 53 +++++----
.../generated/DeleteInstanceConfigSample.java | 14 ++-
...reignKeyConstraintDeleteCascadeSample.java | 34 +++---
.../admin/generated/DropSequenceSample.java | 13 ++-
.../generated/EnableFineGrainedAccess.java | 110 +++++++++---------
.../admin/generated/GetDatabaseDdlSample.java | 24 ++--
.../generated/GetInstanceConfigSample.java | 14 ++-
.../admin/generated/ListDatabaseRoles.java | 27 +++--
.../admin/generated/ListDatabasesSample.java | 33 +++---
.../ListInstanceConfigOperationsSample.java | 14 ++-
.../generated/ListInstanceConfigsSample.java | 14 ++-
.../generated/PgAlterSequenceSample.java | 11 +-
.../generated/PgCaseSensitivitySample.java | 13 +--
.../generated/PgCreateSequenceSample.java | 11 +-
.../admin/generated/PgDropSequenceSample.java | 10 +-
.../generated/PgInterleavedTableSample.java | 14 ++-
.../admin/generated/PgSpannerSample.java | 5 +-
.../RestoreBackupWithEncryptionKey.java | 13 ++-
.../admin/generated/SpannerSample.java | 4 +-
.../admin/generated/UpdateDatabaseSample.java | 12 +-
...UpdateDatabaseWithDefaultLeaderSample.java | 15 ++-
.../generated/UpdateInstanceConfigSample.java | 14 ++-
.../admin/generated/SampleTestBaseV2.java | 2 +
38 files changed, 422 insertions(+), 320 deletions(-)
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddAndDropDatabaseRole.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddAndDropDatabaseRole.java
index d88fe72dc8..69a154e882 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddAndDropDatabaseRole.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddAndDropDatabaseRole.java
@@ -18,17 +18,18 @@
// [START spanner_add_and_drop_database_role]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class AddAndDropDatabaseRole {
- static void addAndDropDatabaseRole() throws IOException {
+ static void addAndDropDatabaseRole() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -39,10 +40,14 @@ static void addAndDropDatabaseRole() throws IOException {
}
static void addAndDropDatabaseRole(
- String projectId, String instanceId, String databaseId, String parentRole, String childRole)
- throws IOException {
- final DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
- try {
+ String projectId, String instanceId, String databaseId,
+ String parentRole, String childRole) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
System.out.println("Waiting for role create operation to complete...");
databaseAdminClient.updateDatabaseDdlAsync(
DatabaseName.of(projectId, instanceId, databaseId),
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonColumnSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonColumnSample.java
index 32d1daea2d..69d2c229e2 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonColumnSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonColumnSample.java
@@ -17,15 +17,17 @@
package com.example.spanner.admin.generated;
// [START spanner_add_json_column]
+
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
class AddJsonColumnSample {
- static void addJsonColumn() throws InterruptedException, ExecutionException, IOException {
+ static void addJsonColumn() throws InterruptedException, ExecutionException {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -35,15 +37,20 @@ static void addJsonColumn() throws InterruptedException, ExecutionException, IOE
}
static void addJsonColumn(String projectId, String instanceId, String databaseId)
- throws InterruptedException, ExecutionException, IOException {
- final DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
-
- // Wait for the operation to finish.
- // This will throw an ExecutionException if the operation fails.
- databaseAdminClient.updateDatabaseDdlAsync(
- DatabaseName.of(projectId, instanceId, databaseId),
- ImmutableList.of("ALTER TABLE Venues ADD COLUMN VenueDetails JSON")).get();
- System.out.printf("Successfully added column `VenueDetails`%n");
+ throws InterruptedException, ExecutionException {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ // Wait for the operation to finish.
+ // This will throw an ExecutionException if the operation fails.
+ databaseAdminClient.updateDatabaseDdlAsync(
+ DatabaseName.of(projectId, instanceId, databaseId),
+ ImmutableList.of("ALTER TABLE Venues ADD COLUMN VenueDetails JSON")).get();
+ System.out.printf("Successfully added column `VenueDetails`%n");
+ }
}
}
// [END spanner_add_json_column]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonbColumnSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonbColumnSample.java
index 800c2d3d65..fa5604e65e 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonbColumnSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonbColumnSample.java
@@ -17,15 +17,17 @@
package com.example.spanner.admin.generated;
// [START spanner_postgresql_jsonb_add_column]
+
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
class AddJsonbColumnSample {
- static void addJsonbColumn() throws InterruptedException, ExecutionException, IOException {
+ static void addJsonbColumn() throws InterruptedException, ExecutionException {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -35,16 +37,21 @@ static void addJsonbColumn() throws InterruptedException, ExecutionException, IO
}
static void addJsonbColumn(String projectId, String instanceId, String databaseId)
- throws InterruptedException, ExecutionException, IOException {
- final DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
-
- // JSONB datatype is only supported with PostgreSQL-dialect databases.
- // Wait for the operation to finish.
- // This will throw an ExecutionException if the operation fails.
- databaseAdminClient.updateDatabaseDdlAsync(
- DatabaseName.of(projectId, instanceId, databaseId),
- ImmutableList.of("ALTER TABLE Venues ADD COLUMN VenueDetails JSONB")).get();
- System.out.printf("Successfully added column `VenueDetails`%n");
+ throws InterruptedException, ExecutionException {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ // JSONB datatype is only supported with PostgreSQL-dialect databases.
+ // Wait for the operation to finish.
+ // This will throw an ExecutionException if the operation fails.
+ databaseAdminClient.updateDatabaseDdlAsync(
+ DatabaseName.of(projectId, instanceId, databaseId),
+ ImmutableList.of("ALTER TABLE Venues ADD COLUMN VenueDetails JSONB")).get();
+ System.out.printf("Successfully added column `VenueDetails`%n");
+ }
}
}
// [END spanner_postgresql_jsonb_add_column]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddNumericColumnSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddNumericColumnSample.java
index 191e0377b6..09f4e63216 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddNumericColumnSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddNumericColumnSample.java
@@ -18,15 +18,16 @@
// [START spanner_add_numeric_column]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
class AddNumericColumnSample {
- static void addNumericColumn() throws InterruptedException, ExecutionException, IOException {
+ static void addNumericColumn() throws InterruptedException, ExecutionException {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -36,15 +37,20 @@ static void addNumericColumn() throws InterruptedException, ExecutionException,
}
static void addNumericColumn(String projectId, String instanceId, String databaseId)
- throws InterruptedException, ExecutionException, IOException {
- final DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
-
- // Wait for the operation to finish.
- // This will throw an ExecutionException if the operation fails.
- databaseAdminClient.updateDatabaseDdlAsync(
- DatabaseName.of(projectId, instanceId, databaseId),
- ImmutableList.of("ALTER TABLE Venues ADD COLUMN Revenue NUMERIC")).get();
- System.out.printf("Successfully added column `Revenue`%n");
+ throws InterruptedException, ExecutionException {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ // Wait for the operation to finish.
+ // This will throw an ExecutionException if the operation fails.
+ databaseAdminClient.updateDatabaseDdlAsync(
+ DatabaseName.of(projectId, instanceId, databaseId),
+ ImmutableList.of("ALTER TABLE Venues ADD COLUMN Revenue NUMERIC")).get();
+ System.out.printf("Successfully added column `Revenue`%n");
+ }
}
}
// [END spanner_add_numeric_column]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterSequenceSample.java
index 05ae63a7a5..08b02b4243 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterSequenceSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterSequenceSample.java
@@ -28,7 +28,6 @@
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -36,7 +35,7 @@
public class AlterSequenceSample {
- static void alterSequence() throws IOException {
+ static void alterSequence() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -44,11 +43,10 @@ static void alterSequence() throws IOException {
alterSequence(projectId, instanceId, databaseId);
}
- static void alterSequence(String projectId, String instanceId, String databaseId)
- throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
+ static void alterSequence(String projectId, String instanceId, String databaseId) {
try (Spanner spanner =
- SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
databaseAdminClient
.updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId, databaseId),
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSample.java
index 5784bfab0c..60f386c75f 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSample.java
@@ -18,14 +18,15 @@
// [START spanner_alter_table_with_foreign_key_delete_cascade]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
class AlterTableWithForeignKeyDeleteCascadeSample {
- static void alterForeignKeyDeleteCascadeConstraint() throws IOException {
+ static void alterForeignKeyDeleteCascadeConstraint() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -35,22 +36,24 @@ static void alterForeignKeyDeleteCascadeConstraint() throws IOException {
}
static void alterForeignKeyDeleteCascadeConstraint(
- String projectId, String instanceId, String databaseId) throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
-
- databaseAdminClient.updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId,
- databaseId),
- ImmutableList.of(
- "ALTER TABLE ShoppingCarts\n"
- + " ADD CONSTRAINT FKShoppingCartsCustomerName\n"
- + " FOREIGN KEY (CustomerName)\n"
- + " REFERENCES Customers(CustomerName)\n"
- + " ON DELETE CASCADE\n"));
- System.out.printf(
- String.format(
- "Altered ShoppingCarts table with FKShoppingCartsCustomerName\n"
- + "foreign key constraint on database %s on instance %s",
- databaseId, instanceId));
+ String projectId, String instanceId, String databaseId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ databaseAdminClient.updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId,
+ databaseId),
+ ImmutableList.of(
+ "ALTER TABLE ShoppingCarts\n"
+ + " ADD CONSTRAINT FKShoppingCartsCustomerName\n"
+ + " FOREIGN KEY (CustomerName)\n"
+ + " REFERENCES Customers(CustomerName)\n"
+ + " ON DELETE CASCADE\n"));
+ System.out.printf(
+ String.format(
+ "Altered ShoppingCarts table with FKShoppingCartsCustomerName\n"
+ + "foreign key constraint on database %s on instance %s",
+ databaseId, instanceId));
+ }
}
}
// [END spanner_alter_table_with_foreign_key_delete_cascade]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java
index ebac469ca7..245abd9429 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java
@@ -19,13 +19,14 @@
// [START spanner_copy_backup]
import com.google.cloud.Timestamp;
+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.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.spanner.admin.database.v1.Backup;
import com.google.spanner.admin.database.v1.BackupName;
import com.google.spanner.admin.database.v1.InstanceName;
-import java.io.IOException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
@@ -34,14 +35,16 @@
public class CopyBackupSample {
- static void copyBackup() throws IOException {
+ 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 (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
copyBackup(databaseAdminClient, projectId, instanceId, sourceBackupId, destinationBackupId);
}
}
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java
index 39f070e639..9152b26a0f 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java
@@ -18,19 +18,18 @@
// [START spanner_create_backup_with_encryption_key]
+import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
-import com.google.cloud.spanner.encryption.EncryptionConfigs;
import com.google.protobuf.Timestamp;
import com.google.spanner.admin.database.v1.Backup;
import com.google.spanner.admin.database.v1.BackupName;
import com.google.spanner.admin.database.v1.CreateBackupEncryptionConfig;
import com.google.spanner.admin.database.v1.CreateBackupEncryptionConfig.EncryptionType;
-import com.google.spanner.admin.database.v1.CreateBackupMetadata;
import com.google.spanner.admin.database.v1.CreateBackupRequest;
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.spanner.admin.database.v1.InstanceName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -39,7 +38,7 @@
public class CreateBackupWithEncryptionKey {
- static void createBackupWithEncryptionKey() throws IOException {
+ static void createBackupWithEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -48,7 +47,9 @@ static void createBackupWithEncryptionKey() throws IOException {
String kmsKeyName =
"projects/" + projectId + "/locations//keyRings//cryptoKeys/";
- try (DatabaseAdminClient adminClient = DatabaseAdminClient.create()) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) {
createBackupWithEncryptionKey(
adminClient,
projectId,
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java
index 2e9d9889f4..fe0265409c 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java
@@ -18,21 +18,22 @@
// [START spanner_create_database_with_encryption_key]
+import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.EncryptionConfig;
import com.google.spanner.admin.database.v1.InstanceName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CreateDatabaseWithEncryptionKey {
- static void createDatabaseWithEncryptionKey() throws IOException {
+ static void createDatabaseWithEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -40,7 +41,9 @@ static void createDatabaseWithEncryptionKey() throws IOException {
String kmsKeyName =
"projects/" + projectId + "/locations//keyRings//cryptoKeys/";
- try (DatabaseAdminClient adminClient = DatabaseAdminClient.create()) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) {
createDatabaseWithEncryptionKey(
adminClient,
projectId,
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSample.java
index 8d26f1ced5..fbea541792 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSample.java
@@ -18,19 +18,20 @@
// [START spanner_create_database_with_version_retention_period]
+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.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.Lists;
import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.InstanceName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
public class CreateDatabaseWithVersionRetentionPeriodSample {
- static void createDatabaseWithVersionRetentionPeriod() throws IOException {
+ static void createDatabaseWithVersionRetentionPeriod() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -42,10 +43,10 @@ static void createDatabaseWithVersionRetentionPeriod() throws IOException {
}
static void createDatabaseWithVersionRetentionPeriod(String projectId,
- String instanceId, String databaseId, String versionRetentionPeriod) throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
-
- try {
+ String instanceId, String databaseId, String versionRetentionPeriod) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
CreateDatabaseRequest request =
CreateDatabaseRequest.newBuilder()
.setParent(InstanceName.of(projectId, instanceId).toString())
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceConfigSample.java
index 49ce023b24..8671c6eda3 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceConfigSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceConfigSample.java
@@ -18,13 +18,14 @@
// [START spanner_create_instance_config]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
import com.google.spanner.admin.instance.v1.CreateInstanceConfigRequest;
import com.google.spanner.admin.instance.v1.InstanceConfig;
import com.google.spanner.admin.instance.v1.InstanceConfigName;
import com.google.spanner.admin.instance.v1.ProjectName;
import com.google.spanner.admin.instance.v1.ReplicaInfo;
-import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -34,7 +35,7 @@
class CreateInstanceConfigSample {
- static void createInstanceConfig() throws IOException {
+ static void createInstanceConfig() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String baseInstanceConfigId = "nam11";
@@ -44,8 +45,13 @@ static void createInstanceConfig() throws IOException {
}
static void createInstanceConfig(
- String projectId, String baseInstanceConfigId, String instanceConfigId) throws IOException {
- try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
+ String projectId, String baseInstanceConfigId, String instanceConfigId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
final InstanceConfigName baseInstanceConfigName = InstanceConfigName.of(projectId,
baseInstanceConfigId);
final InstanceConfig baseConfig =
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceExample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceExample.java
index 925b098499..e53b46ede2 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceExample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceExample.java
@@ -18,26 +18,25 @@
//[START spanner_create_instance]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
import com.google.spanner.admin.instance.v1.CreateInstanceRequest;
import com.google.spanner.admin.instance.v1.Instance;
import com.google.spanner.admin.instance.v1.InstanceConfigName;
import com.google.spanner.admin.instance.v1.ProjectName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
class CreateInstanceExample {
- static void createInstance() throws IOException {
+ static void createInstance() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
createInstance(projectId, instanceId);
}
- static void createInstance(String projectId, String instanceId) throws IOException {
- InstanceAdminClient instanceAdminClient = InstanceAdminClient.create();
-
+ static void createInstance(String projectId, String instanceId) {
// Set Instance configuration.
int nodeCount = 2;
String displayName = "Descriptive name";
@@ -50,7 +49,14 @@ static void createInstance(String projectId, String instanceId) throws IOExcepti
.setConfig(
InstanceConfigName.of(projectId, "regional-us-central1").toString())
.build();
- try {
+
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
+
// Wait for the createInstance operation to finish.
Instance createdInstance = instanceAdminClient.createInstanceAsync(
CreateInstanceRequest.newBuilder()
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigExample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigExample.java
index aa785fe57d..3faaced75b 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigExample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigExample.java
@@ -18,26 +18,32 @@
// [START spanner_create_instance_with_autoscaling_config]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
import com.google.spanner.admin.instance.v1.AutoscalingConfig;
import com.google.spanner.admin.instance.v1.CreateInstanceRequest;
import com.google.spanner.admin.instance.v1.Instance;
import com.google.spanner.admin.instance.v1.InstanceConfigName;
import com.google.spanner.admin.instance.v1.ProjectName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
class CreateInstanceWithAutoscalingConfigExample {
- static void createInstance() throws IOException {
+ static void createInstance() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
createInstance(projectId, instanceId);
}
- static void createInstance(String projectId, String instanceId) throws IOException {
- try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
+ static void createInstance(String projectId, String instanceId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
// Set Instance configuration.
String configId = "regional-us-central1";
String displayName = "Descriptive name";
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsExample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsExample.java
index 9aa0487098..2f6b6d9887 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsExample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsExample.java
@@ -18,24 +18,31 @@
//[START spanner_create_instance_with_processing_units]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
import com.google.spanner.admin.instance.v1.CreateInstanceRequest;
import com.google.spanner.admin.instance.v1.Instance;
import com.google.spanner.admin.instance.v1.InstanceConfigName;
import com.google.spanner.admin.instance.v1.ProjectName;
-import java.io.IOException;
class CreateInstanceWithProcessingUnitsExample {
- static void createInstance() throws IOException {
+ static void createInstance() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
createInstance(projectId, instanceId);
}
- static void createInstance(String projectId, String instanceId) throws IOException {
- try (InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
+ static void createInstance(String projectId, String instanceId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
+
// Set Instance configuration.
String configId = "regional-us-central1";
// This will create an instance with the processing power of 0.2 nodes.
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateSequenceSample.java
index 6614d988b7..d49eacd92e 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateSequenceSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateSequenceSample.java
@@ -28,7 +28,6 @@
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -36,7 +35,7 @@
public class CreateSequenceSample {
- static void createSequence() throws IOException {
+ static void createSequence() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -44,13 +43,11 @@ static void createSequence() throws IOException {
createSequence(projectId, instanceId, databaseId);
}
- static void createSequence(String projectId, String instanceId, String databaseId)
- throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
+ static void createSequence(String projectId, String instanceId, String databaseId) {
try (Spanner spanner =
- SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
-
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
databaseAdminClient
.updateDatabaseDdlAsync(
DatabaseName.of(projectId, instanceId, databaseId),
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateTableWithForeignKeyDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateTableWithForeignKeyDeleteCascadeSample.java
index cc46f1e214..84d09792c1 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateTableWithForeignKeyDeleteCascadeSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateTableWithForeignKeyDeleteCascadeSample.java
@@ -18,15 +18,15 @@
// [START spanner_create_table_with_foreign_key_delete_cascade]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
-import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
class CreateTableWithForeignKeyDeleteCascadeSample {
- static void createForeignKeyDeleteCascadeConstraint() throws IOException {
+ static void createForeignKeyDeleteCascadeConstraint() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -36,28 +36,31 @@ static void createForeignKeyDeleteCascadeConstraint() throws IOException {
}
static void createForeignKeyDeleteCascadeConstraint(
- String projectId, String instanceId, String databaseId) throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
- databaseAdminClient.updateDatabaseDdlAsync(
- DatabaseName.of(projectId, instanceId, databaseId),
- ImmutableList.of(
- "CREATE TABLE Customers (\n"
- + " CustomerId INT64 NOT NULL,\n"
- + " CustomerName STRING(62) NOT NULL,\n"
- + " ) PRIMARY KEY (CustomerId)",
- "CREATE TABLE ShoppingCarts (\n"
- + " CartId INT64 NOT NULL,\n"
- + " CustomerId INT64 NOT NULL,\n"
- + " CustomerName STRING(62) NOT NULL,\n"
- + " CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId)\n"
- + " REFERENCES Customers (CustomerId) ON DELETE CASCADE\n"
- + " ) PRIMARY KEY (CartId)\n"));
-
- System.out.printf(
- String.format(
- "Created Customers and ShoppingCarts table with FKShoppingCartsCustomerId\n"
- + "foreign key constraint on database %s on instance %s\n",
- databaseId, instanceId));
+ String projectId, String instanceId, String databaseId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ databaseAdminClient.updateDatabaseDdlAsync(
+ DatabaseName.of(projectId, instanceId, databaseId),
+ ImmutableList.of(
+ "CREATE TABLE Customers (\n"
+ + " CustomerId INT64 NOT NULL,\n"
+ + " CustomerName STRING(62) NOT NULL,\n"
+ + " ) PRIMARY KEY (CustomerId)",
+ "CREATE TABLE ShoppingCarts (\n"
+ + " CartId INT64 NOT NULL,\n"
+ + " CustomerId INT64 NOT NULL,\n"
+ + " CustomerName STRING(62) NOT NULL,\n"
+ + " CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId)\n"
+ + " REFERENCES Customers (CustomerId) ON DELETE CASCADE\n"
+ + " ) PRIMARY KEY (CartId)\n"));
+
+ System.out.printf(
+ String.format(
+ "Created Customers and ShoppingCarts table with FKShoppingCartsCustomerId\n"
+ + "foreign key constraint on database %s on instance %s\n",
+ databaseId, instanceId));
+ }
}
}
// [END spanner_create_table_with_foreign_key_delete_cascade]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/DeleteInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/DeleteInstanceConfigSample.java
index 1fa9aee586..479ca6f2ff 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/DeleteInstanceConfigSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/DeleteInstanceConfigSample.java
@@ -18,23 +18,29 @@
// [START spanner_delete_instance_config]
+import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
import com.google.spanner.admin.instance.v1.DeleteInstanceConfigRequest;
import com.google.spanner.admin.instance.v1.InstanceConfigName;
-import java.io.IOException;
class DeleteInstanceConfigSample {
- static void deleteInstanceConfig() throws IOException {
+ static void deleteInstanceConfig() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceConfigId = "custom-user-config";
deleteInstanceConfig(projectId, instanceConfigId);
}
- static void deleteInstanceConfig(String projectId, String instanceConfigId) throws IOException {
- try (final InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
+ static void deleteInstanceConfig(String projectId, String instanceConfigId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
final InstanceConfigName instanceConfigName = InstanceConfigName.of(projectId,
instanceConfigId);
final DeleteInstanceConfigRequest request =
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSample.java
index 7d569acea8..991216a770 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSample.java
@@ -18,14 +18,15 @@
// [START spanner_drop_foreign_key_constraint_delete_cascade]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
class DropForeignKeyConstraintDeleteCascadeSample {
- static void deleteForeignKeyDeleteCascadeConstraint() throws IOException {
+ static void deleteForeignKeyDeleteCascadeConstraint() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -35,19 +36,22 @@ static void deleteForeignKeyDeleteCascadeConstraint() throws IOException {
}
static void deleteForeignKeyDeleteCascadeConstraint(
- String projectId, String instanceId, String databaseId) throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
- databaseAdminClient.updateDatabaseDdlAsync(
- DatabaseName.of(projectId, instanceId, databaseId),
- ImmutableList.of(
- "ALTER TABLE ShoppingCarts\n"
- + " DROP CONSTRAINT FKShoppingCartsCustomerName\n"));
-
- System.out.printf(
- String.format(
- "Altered ShoppingCarts table to drop FKShoppingCartsCustomerName\n"
- + "foreign key constraint on database %s on instance %s\n",
- databaseId, instanceId));
+ String projectId, String instanceId, String databaseId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ databaseAdminClient.updateDatabaseDdlAsync(
+ DatabaseName.of(projectId, instanceId, databaseId),
+ ImmutableList.of(
+ "ALTER TABLE ShoppingCarts\n"
+ + " DROP CONSTRAINT FKShoppingCartsCustomerName\n"));
+
+ System.out.printf(
+ String.format(
+ "Altered ShoppingCarts table to drop FKShoppingCartsCustomerName\n"
+ + "foreign key constraint on database %s on instance %s\n",
+ databaseId, instanceId));
+ }
}
}
// [END spanner_drop_foreign_key_constraint_delete_cascade]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropSequenceSample.java
index f9917677de..ff3522484f 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropSequenceSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropSequenceSample.java
@@ -18,18 +18,19 @@
// [START spanner_drop_sequence]
+import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class DropSequenceSample {
- static void dropSequence() throws IOException {
+ static void dropSequence() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -37,10 +38,10 @@ static void dropSequence() throws IOException {
dropSequence(projectId, instanceId, databaseId);
}
- static void dropSequence(String projectId, String instanceId, String databaseId)
- throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
- try {
+ static void dropSequence(String projectId, String instanceId, String databaseId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
databaseAdminClient
.updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId, databaseId),
ImmutableList.of(
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/EnableFineGrainedAccess.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/EnableFineGrainedAccess.java
index 7f66992cad..6538ba87d8 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/EnableFineGrainedAccess.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/EnableFineGrainedAccess.java
@@ -18,6 +18,8 @@
// [START spanner_enable_fine_grained_access]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.iam.v1.Binding;
@@ -27,11 +29,10 @@
import com.google.iam.v1.SetIamPolicyRequest;
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.type.Expr;
-import java.io.IOException;
public class EnableFineGrainedAccess {
- static void enableFineGrainedAccess() throws IOException {
+ static void enableFineGrainedAccess() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -48,59 +49,62 @@ static void enableFineGrainedAccess(
String databaseId,
String iamMember,
String title,
- String role) throws IOException {
- final DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
- final GetPolicyOptions options =
- GetPolicyOptions.newBuilder().setRequestedPolicyVersion(3).build();
- final GetIamPolicyRequest getRequest =
- GetIamPolicyRequest.newBuilder()
- .setResource(DatabaseName.of(projectId, instanceId, databaseId).toString())
- .setOptions(options).build();
- final Policy policy = databaseAdminClient.getIamPolicy(getRequest);
- int policyVersion = policy.getVersion();
- // The policy in the response from getDatabaseIAMPolicy might use the policy version
- // that you specified, or it might use a lower policy version. For example, if you
- // specify version 3, but the policy has no conditional role bindings, the response
- // uses version 1. Valid values are 0, 1, and 3.
- if (policy.getVersion() < 3) {
- // conditional role bindings work with policy version 3
- policyVersion = 3;
- }
+ String role) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ final GetPolicyOptions options =
+ GetPolicyOptions.newBuilder().setRequestedPolicyVersion(3).build();
+ final GetIamPolicyRequest getRequest =
+ GetIamPolicyRequest.newBuilder()
+ .setResource(DatabaseName.of(projectId, instanceId, databaseId).toString())
+ .setOptions(options).build();
+ final Policy policy = databaseAdminClient.getIamPolicy(getRequest);
+ int policyVersion = policy.getVersion();
+ // The policy in the response from getDatabaseIAMPolicy might use the policy version
+ // that you specified, or it might use a lower policy version. For example, if you
+ // specify version 3, but the policy has no conditional role bindings, the response
+ // uses version 1. Valid values are 0, 1, and 3.
+ if (policy.getVersion() < 3) {
+ // conditional role bindings work with policy version 3
+ policyVersion = 3;
+ }
- Binding binding1 =
- Binding.newBuilder()
- .setRole("roles/spanner.fineGrainedAccessUser")
- .addAllMembers(ImmutableList.of(iamMember))
- .build();
+ Binding binding1 =
+ Binding.newBuilder()
+ .setRole("roles/spanner.fineGrainedAccessUser")
+ .addAllMembers(ImmutableList.of(iamMember))
+ .build();
- Binding binding2 =
- Binding.newBuilder()
- .setRole("roles/spanner.databaseRoleUser")
- .setCondition(
- Expr.newBuilder().setDescription(title).setExpression(
- String.format("resource.name.endsWith(\"/databaseRoles/%s\")", role)
- ).setTitle(title).build())
- .addAllMembers(ImmutableList.of(iamMember))
- .build();
- ImmutableList bindings =
- ImmutableList.builder()
- .addAll(policy.getBindingsList())
- .add(binding1)
- .add(binding2)
- .build();
- Policy policyWithConditions =
- Policy.newBuilder()
- .setVersion(policyVersion)
- .setEtag(policy.getEtag())
- .addAllBindings(bindings)
- .build();
- final SetIamPolicyRequest setRequest =
- SetIamPolicyRequest.newBuilder()
- .setResource(DatabaseName.of(projectId, instanceId, databaseId).toString())
- .setPolicy(policyWithConditions).build();
- final Policy response = databaseAdminClient.setIamPolicy(setRequest);
- System.out.printf(
- "Enabled fine-grained access in IAM with version %d%n", response.getVersion());
+ Binding binding2 =
+ Binding.newBuilder()
+ .setRole("roles/spanner.databaseRoleUser")
+ .setCondition(
+ Expr.newBuilder().setDescription(title).setExpression(
+ String.format("resource.name.endsWith(\"/databaseRoles/%s\")", role)
+ ).setTitle(title).build())
+ .addAllMembers(ImmutableList.of(iamMember))
+ .build();
+ ImmutableList bindings =
+ ImmutableList.builder()
+ .addAll(policy.getBindingsList())
+ .add(binding1)
+ .add(binding2)
+ .build();
+ Policy policyWithConditions =
+ Policy.newBuilder()
+ .setVersion(policyVersion)
+ .setEtag(policy.getEtag())
+ .addAllBindings(bindings)
+ .build();
+ final SetIamPolicyRequest setRequest =
+ SetIamPolicyRequest.newBuilder()
+ .setResource(DatabaseName.of(projectId, instanceId, databaseId).toString())
+ .setPolicy(policyWithConditions).build();
+ final Policy response = databaseAdminClient.setIamPolicy(setRequest);
+ System.out.printf(
+ "Enabled fine-grained access in IAM with version %d%n", response.getVersion());
+ }
}
}
// [END spanner_enable_fine_grained_access]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetDatabaseDdlSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetDatabaseDdlSample.java
index 250136c1fb..5e666ee4f1 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetDatabaseDdlSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetDatabaseDdlSample.java
@@ -18,14 +18,15 @@
//[START spanner_get_database_ddl]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.spanner.admin.database.v1.GetDatabaseDdlResponse;
-import java.io.IOException;
public class GetDatabaseDdlSample {
- static void getDatabaseDdl() throws IOException {
+ static void getDatabaseDdl() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -34,15 +35,16 @@ static void getDatabaseDdl() throws IOException {
}
static void getDatabaseDdl(
- String projectId, String instanceId, String databaseId) throws IOException {
-
- final DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
-
- final GetDatabaseDdlResponse response =
- databaseAdminClient.getDatabaseDdl(DatabaseName.of(projectId, instanceId, databaseId));
- System.out.println("Retrieved database DDL for " + databaseId);
- for (String ddl : response.getStatementsList()) {
- System.out.println(ddl);
+ String projectId, String instanceId, String databaseId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ final GetDatabaseDdlResponse response =
+ databaseAdminClient.getDatabaseDdl(DatabaseName.of(projectId, instanceId, databaseId));
+ System.out.println("Retrieved database DDL for " + databaseId);
+ for (String ddl : response.getStatementsList()) {
+ System.out.println(ddl);
+ }
}
}
}
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetInstanceConfigSample.java
index 5fc1b90747..334741c3d3 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetInstanceConfigSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetInstanceConfigSample.java
@@ -18,22 +18,28 @@
//[START spanner_get_instance_config]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
import com.google.spanner.admin.instance.v1.InstanceConfig;
import com.google.spanner.admin.instance.v1.InstanceConfigName;
-import java.io.IOException;
public class GetInstanceConfigSample {
- static void getInstanceConfig() throws IOException {
+ static void getInstanceConfig() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceConfigId = "nam6";
getInstanceConfig(projectId, instanceConfigId);
}
- static void getInstanceConfig(String projectId, String instanceConfigId) throws IOException {
- try (final InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
+ static void getInstanceConfig(String projectId, String instanceConfigId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
final InstanceConfigName instanceConfigName = InstanceConfigName.of(projectId,
instanceConfigId);
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabaseRoles.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabaseRoles.java
index d5d8859673..7fea5a9c55 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabaseRoles.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabaseRoles.java
@@ -18,16 +18,17 @@
// [START spanner_list_database_roles]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListDatabaseRolesPage;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListDatabaseRolesPagedResponse;
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.spanner.admin.database.v1.DatabaseRole;
-import java.io.IOException;
public class ListDatabaseRoles {
- static void listDatabaseRoles() throws IOException {
+ static void listDatabaseRoles() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -35,16 +36,18 @@ static void listDatabaseRoles() throws IOException {
listDatabaseRoles(projectId, instanceId, databaseId);
}
- static void listDatabaseRoles(String projectId, String instanceId, String databaseId)
- throws IOException {
- final DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
- DatabaseName databaseName = DatabaseName.of(projectId, instanceId, databaseId);
- ListDatabaseRolesPagedResponse response
- = databaseAdminClient.listDatabaseRoles(databaseName);
- System.out.println("List of Database roles");
- for (ListDatabaseRolesPage page : response.iteratePages()) {
- for (DatabaseRole role : page.iterateAll()) {
- System.out.printf("Obtained role %s%n", role.getName());
+ static void listDatabaseRoles(String projectId, String instanceId, String databaseId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ DatabaseName databaseName = DatabaseName.of(projectId, instanceId, databaseId);
+ ListDatabaseRolesPagedResponse response
+ = databaseAdminClient.listDatabaseRoles(databaseName);
+ System.out.println("List of Database roles");
+ for (ListDatabaseRolesPage page : response.iteratePages()) {
+ for (DatabaseRole role : page.iterateAll()) {
+ System.out.printf("Obtained role %s%n", role.getName());
+ }
}
}
}
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabasesSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabasesSample.java
index 7518064f2c..703e89b9f1 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabasesSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabasesSample.java
@@ -18,35 +18,38 @@
//[START spanner_list_databases]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListDatabasesPage;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.ListDatabasesPagedResponse;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.InstanceName;
-import java.io.IOException;
public class ListDatabasesSample {
- static void listDatabases() throws IOException {
+ static void listDatabases() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
listDatabases(projectId, instanceId);
}
- static void listDatabases(String projectId, String instanceId) throws IOException {
- final DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
-
- ListDatabasesPagedResponse response =
- databaseAdminClient.listDatabases(InstanceName.of(projectId, instanceId));
-
- System.out.println("Databases for projects/" + projectId + "/instances/" + instanceId);
-
- for (ListDatabasesPage page : response.iteratePages()) {
- for (Database database : page.iterateAll()) {
- final String defaultLeader = database.getDefaultLeader().equals("")
- ? "" : "(default leader = " + database.getDefaultLeader() + ")";
- System.out.println("\t" + database.getName() + " " + defaultLeader);
+ static void listDatabases(String projectId, String instanceId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+ ListDatabasesPagedResponse response =
+ databaseAdminClient.listDatabases(InstanceName.of(projectId, instanceId));
+
+ System.out.println("Databases for projects/" + projectId + "/instances/" + instanceId);
+
+ for (ListDatabasesPage page : response.iteratePages()) {
+ for (Database database : page.iterateAll()) {
+ final String defaultLeader = database.getDefaultLeader().equals("")
+ ? "" : "(default leader = " + database.getDefaultLeader() + ")";
+ System.out.println("\t" + database.getName() + " " + defaultLeader);
+ }
}
}
}
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigOperationsSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigOperationsSample.java
index 6970c50069..cbfd1908d4 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigOperationsSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigOperationsSample.java
@@ -18,24 +18,30 @@
// [START spanner_list_instance_config_operations]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
import com.google.longrunning.Operation;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.spanner.admin.instance.v1.CreateInstanceConfigMetadata;
import com.google.spanner.admin.instance.v1.ListInstanceConfigOperationsRequest;
import com.google.spanner.admin.instance.v1.ProjectName;
-import java.io.IOException;
public class ListInstanceConfigOperationsSample {
- static void listInstanceConfigOperations() throws IOException {
+ static void listInstanceConfigOperations() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
listInstanceConfigOperations(projectId);
}
- static void listInstanceConfigOperations(String projectId) throws IOException {
- try (final InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
+ static void listInstanceConfigOperations(String projectId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
final ProjectName projectName = ProjectName.of(projectId);
System.out.printf(
"Getting list of instance config operations for project %s...\n",
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigsSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigsSample.java
index 92a0c7014e..f2d6f5fba6 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigsSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigsSample.java
@@ -18,21 +18,27 @@
//[START spanner_list_instance_configs]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
import com.google.spanner.admin.instance.v1.InstanceConfig;
import com.google.spanner.admin.instance.v1.ProjectName;
-import java.io.IOException;
public class ListInstanceConfigsSample {
- static void listInstanceConfigs() throws IOException {
+ static void listInstanceConfigs() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
listInstanceConfigs(projectId);
}
- static void listInstanceConfigs(String projectId) throws IOException {
- try (final InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
+ static void listInstanceConfigs(String projectId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
final ProjectName projectName = ProjectName.of(projectId);
for (InstanceConfig instanceConfig :
instanceAdminClient.listInstanceConfigs(projectName).iterateAll()) {
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgAlterSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgAlterSequenceSample.java
index 39a4cd543a..1168bdcfad 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgAlterSequenceSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgAlterSequenceSample.java
@@ -28,7 +28,6 @@
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -36,7 +35,7 @@
public class PgAlterSequenceSample {
- static void pgAlterSequence() throws IOException {
+ static void pgAlterSequence() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -44,11 +43,11 @@ static void pgAlterSequence() throws IOException {
pgAlterSequence(projectId, instanceId, databaseId);
}
- static void pgAlterSequence(String projectId, String instanceId, String databaseId)
- throws IOException {
+ static void pgAlterSequence(String projectId, String instanceId, String databaseId) {
try (Spanner spanner =
- SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
- final DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
+
databaseAdminClient
.updateDatabaseDdlAsync(
DatabaseName.of(projectId, instanceId, databaseId),
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCaseSensitivitySample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCaseSensitivitySample.java
index 951b46446f..8fbaf6a9c0 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCaseSensitivitySample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCaseSensitivitySample.java
@@ -29,13 +29,12 @@
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.Lists;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
public class PgCaseSensitivitySample {
- static void pgCaseSensitivity() throws IOException {
+ static void pgCaseSensitivity() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -43,15 +42,11 @@ static void pgCaseSensitivity() throws IOException {
pgCaseSensitivity(projectId, instanceId, databaseId);
}
- static void pgCaseSensitivity(String projectId, String instanceId, String databaseId)
- throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
+ static void pgCaseSensitivity(String projectId, String instanceId, String databaseId) {
try (Spanner spanner =
- SpannerOptions.newBuilder()
- .setProjectId(projectId)
- .build()
- .getService()) {
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
// Spanner PostgreSQL follows the case sensitivity rules of PostgreSQL. This means that:
// 1. Identifiers that are not double-quoted are folded to lower case.
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCreateSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCreateSequenceSample.java
index 6e7d9c34c5..2148427ccd 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCreateSequenceSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCreateSequenceSample.java
@@ -28,7 +28,6 @@
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -36,7 +35,7 @@
public class PgCreateSequenceSample {
- static void pgCreateSequence() throws IOException {
+ static void pgCreateSequence() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -44,12 +43,10 @@ static void pgCreateSequence() throws IOException {
pgCreateSequence(projectId, instanceId, databaseId);
}
- static void pgCreateSequence(String projectId, String instanceId, String databaseId)
- throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
-
+ static void pgCreateSequence(String projectId, String instanceId, String databaseId) {
try (Spanner spanner =
- SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
databaseAdminClient
.updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId, databaseId).toString(),
ImmutableList.of(
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgDropSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgDropSequenceSample.java
index dec927d20a..de856f51a3 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgDropSequenceSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgDropSequenceSample.java
@@ -24,14 +24,13 @@
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class PgDropSequenceSample {
- static void pgDropSequence() throws IOException {
+ static void pgDropSequence() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -39,12 +38,11 @@ static void pgDropSequence() throws IOException {
pgDropSequence(projectId, instanceId, databaseId);
}
- static void pgDropSequence(String projectId, String instanceId, String databaseId)
- throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
+ static void pgDropSequence(String projectId, String instanceId, String databaseId) {
try (Spanner spanner =
- SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
databaseAdminClient
.updateDatabaseDdlAsync(
DatabaseName.of(projectId, instanceId, databaseId),
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgInterleavedTableSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgInterleavedTableSample.java
index 14af53dc5a..754920c8dd 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgInterleavedTableSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgInterleavedTableSample.java
@@ -18,16 +18,17 @@
// [START spanner_postgresql_interleaved_table]
+import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
public class PgInterleavedTableSample {
- static void pgInterleavedTable() throws IOException {
+ static void pgInterleavedTable() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -35,10 +36,11 @@ static void pgInterleavedTable() throws IOException {
pgInterleavedTable(projectId, instanceId, databaseId);
}
- static void pgInterleavedTable(String projectId, String instanceId, String databaseId)
- throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
- try {
+ static void pgInterleavedTable(String projectId, String instanceId, String databaseId) {
+
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
// The Spanner PostgreSQL dialect extends the PostgreSQL dialect with certain Spanner
// specific features, such as interleaved tables.
// See https://cloud.google.com/spanner/docs/postgresql/data-definition-language#create_table
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java
index b2e6eafb0f..c4e319c66c 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java
@@ -26,7 +26,6 @@
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;
@@ -1544,7 +1543,7 @@ static void printUsageAndExit() {
System.exit(1);
}
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) {
if (args.length != 3) {
printUsageAndExit();
}
@@ -1574,7 +1573,7 @@ public static void main(String[] args) throws Exception {
// [START spanner_init_client]
DatabaseClient dbClient = spanner.getDatabaseClient(db);
- dbAdminClient = DatabaseAdminClient.create();
+ dbAdminClient = spanner.createDatabaseAdminClient();
// [END spanner_init_client]
// Use client here...
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java
index 9c2ef5b3a7..3c129860f0 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java
@@ -20,20 +20,20 @@
import static com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION;
+import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.spanner.admin.database.v1.BackupName;
import com.google.spanner.admin.database.v1.Database;
-import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.spanner.admin.database.v1.InstanceName;
import com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig;
import com.google.spanner.admin.database.v1.RestoreDatabaseRequest;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
public class RestoreBackupWithEncryptionKey {
- static void restoreBackupWithEncryptionKey() throws IOException {
+ static void restoreBackupWithEncryptionKey() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceId = "my-instance";
@@ -42,7 +42,9 @@ static void restoreBackupWithEncryptionKey() throws IOException {
String kmsKeyName =
"projects/" + projectId + "/locations//keyRings//cryptoKeys/";
- try (DatabaseAdminClient adminClient = DatabaseAdminClient.create()) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) {
restoreBackupWithEncryptionKey(
adminClient,
projectId,
@@ -65,7 +67,8 @@ static Void restoreBackupWithEncryptionKey(DatabaseAdminClient adminClient,
Database database;
try {
System.out.println("Waiting for operation to complete...");
- database = adminClient.restoreDatabaseAsync(request).get();;
+ database = adminClient.restoreDatabaseAsync(request).get();
+ ;
} catch (ExecutionException e) {
// If the operation failed during execution, expose the cause.
throw SpannerExceptionFactory.asSpannerException(e.getCause());
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java
index 6e805dc132..7cc4af7c74 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java
@@ -2225,7 +2225,7 @@ static void printUsageAndExit() {
System.exit(1);
}
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) {
if (args.length != 3 && args.length != 4) {
printUsageAndExit();
}
@@ -2254,7 +2254,7 @@ public static void main(String[] args) throws Exception {
// [START init_client]
DatabaseClient dbClient = spanner.getDatabaseClient(db);
- dbAdminClient = DatabaseAdminClient.create();
+ dbAdminClient = spanner.createDatabaseAdminClient();
// Use client here...
// [END init_client]
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseSample.java
index 958be6e20a..4e4f76d700 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseSample.java
@@ -19,7 +19,9 @@
// [START spanner_update_database]
import com.google.api.gax.longrunning.OperationFuture;
+import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.Lists;
import com.google.protobuf.FieldMask;
@@ -27,14 +29,13 @@
import com.google.spanner.admin.database.v1.DatabaseName;
import com.google.spanner.admin.database.v1.UpdateDatabaseMetadata;
import com.google.spanner.admin.database.v1.UpdateDatabaseRequest;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class UpdateDatabaseSample {
- static void updateDatabase() throws IOException {
+ static void updateDatabase() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -44,9 +45,10 @@ static void updateDatabase() throws IOException {
}
static void updateDatabase(
- String projectId, String instanceId, String databaseId) throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
- try {
+ String projectId, String instanceId, String databaseId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
final Database database =
Database.newBuilder()
.setName(DatabaseName.of(projectId, instanceId, databaseId).toString())
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSample.java
index 5306358714..26c252ac1e 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSample.java
@@ -18,18 +18,18 @@
//[START spanner_update_database_with_default_leader]
-import com.google.api.gax.longrunning.OperationFuture;
+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.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.spanner.admin.database.v1.DatabaseName;
-import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
public class UpdateDatabaseWithDefaultLeaderSample {
- static void updateDatabaseWithDefaultLeader() throws IOException {
+ static void updateDatabaseWithDefaultLeader() {
// TODO(developer): Replace these variables before running the sample.
final String projectId = "my-project";
final String instanceId = "my-instance";
@@ -39,11 +39,10 @@ static void updateDatabaseWithDefaultLeader() throws IOException {
}
static void updateDatabaseWithDefaultLeader(
- String projectId, String instanceId, String databaseId, String defaultLeader)
- throws IOException {
- DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create();
-
- try {
+ String projectId, String instanceId, String databaseId, String defaultLeader) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
+ DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
databaseAdminClient
.updateDatabaseDdlAsync(
DatabaseName.of(projectId, instanceId, databaseId),
diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateInstanceConfigSample.java
index 1fb5cd0e6f..e74d8071c2 100644
--- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateInstanceConfigSample.java
+++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateInstanceConfigSample.java
@@ -18,28 +18,34 @@
// [START spanner_update_instance_config]
+import com.google.cloud.spanner.Spanner;
+import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.FieldMask;
import com.google.spanner.admin.instance.v1.InstanceConfig;
import com.google.spanner.admin.instance.v1.InstanceConfigName;
import com.google.spanner.admin.instance.v1.UpdateInstanceConfigRequest;
-import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
class UpdateInstanceConfigSample {
- static void updateInstanceConfig() throws IOException {
+ static void updateInstanceConfig() {
// TODO(developer): Replace these variables before running the sample.
String projectId = "my-project";
String instanceConfigId = "custom-instance-config";
updateInstanceConfig(projectId, instanceConfigId);
}
- static void updateInstanceConfig(String projectId, String instanceConfigId) throws IOException {
- try (final InstanceAdminClient instanceAdminClient = InstanceAdminClient.create()) {
+ static void updateInstanceConfig(String projectId, String instanceConfigId) {
+ try (Spanner spanner =
+ SpannerOptions.newBuilder()
+ .setProjectId(projectId)
+ .build()
+ .getService();
+ InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) {
final InstanceConfigName instanceConfigName =
InstanceConfigName.of(projectId, instanceConfigId);
final InstanceConfig instanceConfig =
diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/SampleTestBaseV2.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SampleTestBaseV2.java
index e5ff002110..32e1c26fcb 100644
--- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/SampleTestBaseV2.java
+++ b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SampleTestBaseV2.java
@@ -113,6 +113,7 @@ public static void afterClass() throws InterruptedException {
}
}
for (String backupId : idGenerator.getBackupIds()) {
+ System.out.println("Trying to delete " + backupId);
try {
// If the backup is not found, it is ignored (no exception is thrown)
databaseAdminClient.deleteBackup(
@@ -125,6 +126,7 @@ public static void afterClass() throws InterruptedException {
}
}
for (String configId : idGenerator.getInstanceConfigIds()) {
+ System.out.println("Trying to delete " + configId);
try {
// If the config is not found, it is ignored (no exception is thrown)
instanceAdminClient.deleteInstanceConfig(getInstanceConfigName(projectId, configId));
From 4f7d28bdbabce5faf6d37b8a3224235bb1012f78 Mon Sep 17 00:00:00 2001
From: Hengfeng Li
Date: Tue, 27 Feb 2024 21:48:14 +1100
Subject: [PATCH 06/16] chore: add support for OpenTelemetry metrics to
Connection API (#2896)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* chore: add support for OpenTelemetry metrics to Connection API
* 🦉 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
---
.../spanner/connection/ConnectionOptions.java | 17 ++++++++++
.../cloud/spanner/connection/SpannerPool.java | 12 +++++--
.../spanner/connection/SpannerPoolTest.java | 33 +++++++++++++++++++
3 files changed, 60 insertions(+), 2 deletions(-)
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 268661aef9..8bca0b2834 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
@@ -39,6 +39,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
+import io.opentelemetry.api.OpenTelemetry;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -484,6 +485,7 @@ public static class Builder {
private List statementExecutionInterceptors =
Collections.emptyList();
private SpannerOptionsConfigurator configurator;
+ private OpenTelemetry openTelemetry;
private Builder() {}
@@ -633,6 +635,11 @@ Builder setCredentials(Credentials credentials) {
return this;
}
+ public Builder setOpenTelemetry(OpenTelemetry openTelemetry) {
+ this.openTelemetry = openTelemetry;
+ return this;
+ }
+
/** @return the {@link ConnectionOptions} */
public ConnectionOptions build() {
Preconditions.checkState(this.uri != null, "Connection URI is required");
@@ -691,6 +698,7 @@ public static Builder newBuilder() {
private final boolean retryAbortsInternally;
private final boolean useVirtualThreads;
private final boolean useVirtualGrpcTransportThreads;
+ private final OpenTelemetry openTelemetry;
private final List statementExecutionInterceptors;
private final SpannerOptionsConfigurator configurator;
@@ -792,6 +800,7 @@ private ConnectionOptions(Builder builder) {
this.retryAbortsInternally = parseRetryAbortsInternally(this.uri);
this.useVirtualThreads = parseUseVirtualThreads(this.uri);
this.useVirtualGrpcTransportThreads = parseUseVirtualGrpcTransportThreads(this.uri);
+ this.openTelemetry = builder.openTelemetry;
this.statementExecutionInterceptors =
Collections.unmodifiableList(builder.statementExecutionInterceptors);
this.configurator = builder.configurator;
@@ -856,6 +865,14 @@ private static Integer parseIntegerProperty(String propertyName, String value) {
return null;
}
+ /**
+ * @return an instance of OpenTelemetry. If OpenTelemetry object is not set then null
+ * will be returned.
+ */
+ OpenTelemetry getOpenTelemetry() {
+ return this.openTelemetry;
+ }
+
SpannerOptionsConfigurator getConfigurator() {
return configurator;
}
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 da8da78d92..b4ac7cf3a3 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
@@ -29,6 +29,7 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Ticker;
import io.grpc.ManagedChannelBuilder;
+import io.opentelemetry.api.OpenTelemetry;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -156,6 +157,7 @@ static class SpannerPoolKey {
private final String databaseRole;
private final boolean routeToLeader;
private final boolean useVirtualGrpcTransportThreads;
+ private final OpenTelemetry openTelemetry;
@VisibleForTesting
static SpannerPoolKey of(ConnectionOptions options) {
@@ -183,6 +185,7 @@ private SpannerPoolKey(ConnectionOptions options) throws IOException {
this.userAgent = options.getUserAgent();
this.routeToLeader = options.isRouteToLeader();
this.useVirtualGrpcTransportThreads = options.isUseVirtualGrpcTransportThreads();
+ this.openTelemetry = options.getOpenTelemetry();
}
@Override
@@ -201,7 +204,8 @@ public boolean equals(Object o) {
&& Objects.equals(this.userAgent, other.userAgent)
&& Objects.equals(this.routeToLeader, other.routeToLeader)
&& Objects.equals(
- this.useVirtualGrpcTransportThreads, other.useVirtualGrpcTransportThreads);
+ this.useVirtualGrpcTransportThreads, other.useVirtualGrpcTransportThreads)
+ && Objects.equals(this.openTelemetry, other.openTelemetry);
}
@Override
@@ -216,7 +220,8 @@ public int hashCode() {
this.databaseRole,
this.userAgent,
this.routeToLeader,
- this.useVirtualGrpcTransportThreads);
+ this.useVirtualGrpcTransportThreads,
+ this.openTelemetry);
}
}
@@ -349,6 +354,9 @@ Spanner createSpanner(SpannerPoolKey key, ConnectionOptions options) {
.setDatabaseRole(options.getDatabaseRole())
.setCredentials(options.getCredentials());
builder.setSessionPoolOption(key.sessionPoolOptions);
+ if (key.openTelemetry != null) {
+ builder.setOpenTelemetry(key.openTelemetry);
+ }
if (key.numChannels != null) {
builder.setNumChannels(key.numChannels);
}
diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java
index ed68f05c5f..b11c1f19be 100644
--- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java
+++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SpannerPoolTest.java
@@ -39,6 +39,8 @@
import com.google.cloud.spanner.connection.SpannerPool.SpannerPoolKey;
import com.google.common.base.Ticker;
import com.google.common.testing.FakeTicker;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.sdk.OpenTelemetrySdk;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
@@ -69,6 +71,10 @@ public class SpannerPoolTest {
private ConnectionOptions options7 = mock(ConnectionOptions.class);
private ConnectionOptions options8 = mock(ConnectionOptions.class);
+ private ConnectionOptions optionsOpenTelemetry1 = mock(ConnectionOptions.class);
+ private ConnectionOptions optionsOpenTelemetry2 = mock(ConnectionOptions.class);
+ private ConnectionOptions optionsOpenTelemetry3 = mock(ConnectionOptions.class);
+
private SpannerPool createSubjectAndMocks() {
return createSubjectAndMocks(0L, Ticker.systemTicker());
}
@@ -83,6 +89,9 @@ Spanner createSpanner(SpannerPoolKey key, ConnectionOptions options) {
}
};
+ OpenTelemetry openTelemetry1 = OpenTelemetrySdk.builder().build();
+ OpenTelemetry openTelemetry2 = OpenTelemetrySdk.builder().build();
+
when(options1.getCredentialsUrl()).thenReturn(credentials1);
when(options1.getProjectId()).thenReturn("test-project-1");
when(options2.getCredentialsUrl()).thenReturn(credentials2);
@@ -101,6 +110,13 @@ Spanner createSpanner(SpannerPoolKey key, ConnectionOptions options) {
when(options8.getProjectId()).thenReturn("test-project-3");
when(options8.isRouteToLeader()).thenReturn(false);
+ when(optionsOpenTelemetry1.getProjectId()).thenReturn("test-project-1");
+ when(optionsOpenTelemetry1.getOpenTelemetry()).thenReturn(openTelemetry1);
+ when(optionsOpenTelemetry2.getProjectId()).thenReturn("test-project-1");
+ when(optionsOpenTelemetry2.getOpenTelemetry()).thenReturn(openTelemetry1);
+ when(optionsOpenTelemetry3.getProjectId()).thenReturn("test-project-1");
+ when(optionsOpenTelemetry3.getOpenTelemetry()).thenReturn(openTelemetry2);
+
return pool;
}
@@ -498,4 +514,21 @@ public void testSpannerPoolKeyEquality() {
assertEquals(key3, key4);
assertNotEquals(key4, key5);
}
+
+ @Test
+ public void testOpenTelemetry() {
+ SpannerPool pool = createSubjectAndMocks();
+ Spanner spanner1;
+ Spanner spanner2;
+
+ // assert equal
+ spanner1 = pool.getSpanner(optionsOpenTelemetry1, connection1);
+ spanner2 = pool.getSpanner(optionsOpenTelemetry2, connection2);
+ assertEquals(spanner1, spanner2);
+
+ // assert not equal
+ spanner1 = pool.getSpanner(optionsOpenTelemetry1, connection1);
+ spanner2 = pool.getSpanner(optionsOpenTelemetry3, connection2);
+ assertNotEquals(spanner1, spanner2);
+ }
}
From 19b79764294e938ad85d02b7c0662db6ec3afeda Mon Sep 17 00:00:00 2001
From: Aravind Pedapudi
Date: Wed, 28 Feb 2024 15:53:14 +0530
Subject: [PATCH 07/16] feat: support float32 type (#2894)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: float32 changes with unit and integration tests
* Update formatting and clirr
* Update the hashCode logic to account for NaN equality
* Prevent FLOAT32 integration tests from running on emulator and production
* Fix integration tests for FLOAT32
* Update float32UntypedParameters test to work with PG dialect too
* Split the parameters test in ITQueryTest into supported + currently-unsupported tests.
* Split the Mutation.isNaN method to make it more readable
* test: added some additional tests
* Update to resolve comments on PR#2894.
Major change: Ensures that the new methods in interfaces do not break for older clients.
Minor changes: remove double cast; remove dependency on Truth assertions; remove unnecessary logic in Mutations::isNaN
* Un-ignore the skipped FLOAT32 tests as the backend fixes have been deployed
* Un-ignore the float32 tests in ITQueryTest
---------
Co-authored-by: Owl Bot
Co-authored-by: Knut Olav Løite
---
.../clirr-ignored-differences.xml | 46 +-
.../cloud/spanner/AbstractResultSet.java | 76 +++-
.../cloud/spanner/AbstractStructReader.java | 51 +++
.../cloud/spanner/ForwardingStructReader.java | 36 ++
.../com/google/cloud/spanner/GrpcStruct.java | 38 +-
.../com/google/cloud/spanner/Mutation.java | 18 +-
.../com/google/cloud/spanner/ResultSets.java | 30 ++
.../java/com/google/cloud/spanner/Struct.java | 20 +
.../google/cloud/spanner/StructReader.java | 54 +++
.../java/com/google/cloud/spanner/Type.java | 17 +-
.../java/com/google/cloud/spanner/Value.java | 198 ++++++++-
.../com/google/cloud/spanner/ValueBinder.java | 25 ++
.../connection/DirectExecuteResultSet.java | 36 ++
.../ReplaceableForwardingResultSet.java | 36 ++
.../AbstractStructReaderTypesTest.java | 36 ++
.../cloud/spanner/GrpcResultSetTest.java | 39 ++
.../cloud/spanner/MockSpannerServiceImpl.java | 4 +
.../google/cloud/spanner/MutationTest.java | 56 ++-
.../spanner/RandomResultSetGenerator.java | 8 +
.../google/cloud/spanner/ReadAsyncTest.java | 5 +-
.../cloud/spanner/ReadFormatTestRunner.java | 6 +
.../google/cloud/spanner/ResultSetsTest.java | 28 ++
.../com/google/cloud/spanner/TypeTest.java | 20 +
.../google/cloud/spanner/ValueBinderTest.java | 16 +
.../com/google/cloud/spanner/ValueTest.java | 151 +++++++
.../connection/ChecksumResultSetTest.java | 14 +
.../cloud/spanner/it/ITAsyncExamplesTest.java | 5 +-
.../cloud/spanner/it/ITFloat32Test.java | 415 ++++++++++++++++++
.../google/cloud/spanner/it/ITQueryTest.java | 83 ++++
29 files changed, 1544 insertions(+), 23 deletions(-)
create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITFloat32Test.java
diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml
index fbbc0153f8..eaf7637b0b 100644
--- a/google-cloud-spanner/clirr-ignored-differences.xml
+++ b/google-cloud-spanner/clirr-ignored-differences.xml
@@ -506,6 +506,48 @@
com.google.cloud.spanner.connection.StatementResult execute(com.google.cloud.spanner.Statement, java.util.Set)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ float getFloat(int)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ float getFloat(java.lang.String)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ float[] getFloatArray(int)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ float[] getFloatArray(java.lang.String)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ java.util.List getFloatList(int)
+
+
+ 7012
+ com/google/cloud/spanner/StructReader
+ java.util.List getFloatList(java.lang.String)
+
+
+ 7013
+ com/google/cloud/spanner/Value
+ float getFloat32()
+
+
+ 7013
+ com/google/cloud/spanner/Value
+ java.util.List getFloat32Array()
+
+
7012
@@ -569,7 +611,7 @@
void setSpan(io.opencensus.trace.Span)void setSpan(com.google.cloud.spanner.ISpan)
-
+
7012
@@ -580,5 +622,5 @@
7012com/google/cloud/spanner/connection/Connectionvoid setDirectedRead(com.google.spanner.v1.DirectedReadOptions)
-
+
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java
index 6cce03e72c..2cf93fb92e 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java
@@ -173,16 +173,44 @@ static double valueProtoToFloat64(com.google.protobuf.Value proto) {
return proto.getNumberValue();
}
+ static float valueProtoToFloat32(com.google.protobuf.Value proto) {
+ if (proto.getKindCase() == KindCase.STRING_VALUE) {
+ switch (proto.getStringValue()) {
+ case "-Infinity":
+ return Float.NEGATIVE_INFINITY;
+ case "Infinity":
+ return Float.POSITIVE_INFINITY;
+ case "NaN":
+ return Float.NaN;
+ default:
+ // Fall-through to handling below to produce an error.
+ }
+ }
+ if (proto.getKindCase() != KindCase.NUMBER_VALUE) {
+ throw newSpannerException(
+ ErrorCode.INTERNAL,
+ "Invalid value for column type "
+ + Type.float32()
+ + " expected NUMBER_VALUE or STRING_VALUE with value one of"
+ + " \"Infinity\", \"-Infinity\", or \"NaN\" but was "
+ + proto.getKindCase()
+ + (proto.getKindCase() == KindCase.STRING_VALUE
+ ? " with value \"" + proto.getStringValue() + "\""
+ : ""));
+ }
+ return (float) proto.getNumberValue();
+ }
+
static NullPointerException throwNotNull(int columnIndex) {
throw new NullPointerException(
"Cannot call array getter for column " + columnIndex + " with null elements");
}
/**
- * Memory-optimized base class for {@code ARRAY} and {@code ARRAY} types. Both of
- * these involve conversions from the type yielded by JSON parsing, which are {@code String} and
- * {@code BigDecimal} respectively. Rather than construct new wrapper objects for each array
- * element, we use primitive arrays and a {@code BitSet} to track nulls.
+ * Memory-optimized base class for {@code ARRAY}, {@code ARRAY} and {@code
+ * ARRAY} types. All of these involve conversions from the type yielded by JSON parsing,
+ * which are {@code String} and {@code BigDecimal} respectively. Rather than construct new wrapper
+ * objects for each array element, we use primitive arrays and a {@code BitSet} to track nulls.
*/
abstract static class PrimitiveArray extends AbstractList {
private final A data;
@@ -264,6 +292,31 @@ Long get(long[] array, int i) {
}
}
+ static class Float32Array extends PrimitiveArray {
+ Float32Array(ListValue protoList) {
+ super(protoList);
+ }
+
+ Float32Array(float[] data, BitSet nulls) {
+ super(data, nulls, data.length);
+ }
+
+ @Override
+ float[] newArray(int size) {
+ return new float[size];
+ }
+
+ @Override
+ void setProto(float[] array, int i, com.google.protobuf.Value protoValue) {
+ array[i] = valueProtoToFloat32(protoValue);
+ }
+
+ @Override
+ Float get(float[] array, int i) {
+ return array[i];
+ }
+ }
+
static class Float64Array extends PrimitiveArray {
Float64Array(ListValue protoList) {
super(protoList);
@@ -306,6 +359,11 @@ protected long getLongInternal(int columnIndex) {
return currRow().getLongInternal(columnIndex);
}
+ @Override
+ protected float getFloatInternal(int columnIndex) {
+ return currRow().getFloatInternal(columnIndex);
+ }
+
@Override
protected double getDoubleInternal(int columnIndex) {
return currRow().getDoubleInternal(columnIndex);
@@ -382,6 +440,16 @@ protected List getLongListInternal(int columnIndex) {
return currRow().getLongListInternal(columnIndex);
}
+ @Override
+ protected float[] getFloatArrayInternal(int columnIndex) {
+ return currRow().getFloatArrayInternal(columnIndex);
+ }
+
+ @Override
+ protected List getFloatListInternal(int columnIndex) {
+ return currRow().getFloatListInternal(columnIndex);
+ }
+
@Override
protected double[] getDoubleArrayInternal(int columnIndex) {
return currRow().getDoubleArrayInternal(columnIndex);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java
index ef6f63d52e..a11c573233 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java
@@ -43,6 +43,10 @@ public abstract class AbstractStructReader implements StructReader {
protected abstract long getLongInternal(int columnIndex);
+ protected float getFloatInternal(int columnIndex) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
protected abstract double getDoubleInternal(int columnIndex);
protected abstract BigDecimal getBigDecimalInternal(int columnIndex);
@@ -94,6 +98,14 @@ protected Value getValueInternal(int columnIndex) {
protected abstract List getLongListInternal(int columnIndex);
+ protected float[] getFloatArrayInternal(int columnIndex) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ protected List getFloatListInternal(int columnIndex) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
protected abstract double[] getDoubleArrayInternal(int columnIndex);
protected abstract List getDoubleListInternal(int columnIndex);
@@ -164,6 +176,19 @@ public long getLong(String columnName) {
return getLongInternal(columnIndex);
}
+ @Override
+ public float getFloat(int columnIndex) {
+ checkNonNullOfType(columnIndex, Type.float32(), columnIndex);
+ return getFloatInternal(columnIndex);
+ }
+
+ @Override
+ public float getFloat(String columnName) {
+ int columnIndex = getColumnIndex(columnName);
+ checkNonNullOfType(columnIndex, Type.float32(), columnName);
+ return getFloatInternal(columnIndex);
+ }
+
@Override
public double getDouble(int columnIndex) {
checkNonNullOfType(columnIndex, Type.float64(), columnIndex);
@@ -368,6 +393,32 @@ public List getLongList(String columnName) {
return getLongListInternal(columnIndex);
}
+ @Override
+ public float[] getFloatArray(int columnIndex) {
+ checkNonNullOfType(columnIndex, Type.array(Type.float32()), columnIndex);
+ return getFloatArrayInternal(columnIndex);
+ }
+
+ @Override
+ public float[] getFloatArray(String columnName) {
+ int columnIndex = getColumnIndex(columnName);
+ checkNonNullOfType(columnIndex, Type.array(Type.float32()), columnName);
+ return getFloatArrayInternal(columnIndex);
+ }
+
+ @Override
+ public List getFloatList(int columnIndex) {
+ checkNonNullOfType(columnIndex, Type.array(Type.float32()), columnIndex);
+ return getFloatListInternal(columnIndex);
+ }
+
+ @Override
+ public List getFloatList(String columnName) {
+ int columnIndex = getColumnIndex(columnName);
+ checkNonNullOfType(columnIndex, Type.array(Type.float32()), columnName);
+ return getFloatListInternal(columnIndex);
+ }
+
@Override
public double[] getDoubleArray(int columnIndex) {
checkNonNullOfType(columnIndex, Type.array(Type.float64()), columnIndex);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java
index 97c39c00a8..b3e37ffcdd 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java
@@ -125,6 +125,18 @@ public long getLong(String columnName) {
return delegate.get().getLong(columnName);
}
+ @Override
+ public float getFloat(int columnIndex) {
+ checkValidState();
+ return delegate.get().getFloat(columnIndex);
+ }
+
+ @Override
+ public float getFloat(String columnName) {
+ checkValidState();
+ return delegate.get().getFloat(columnName);
+ }
+
@Override
public double getDouble(int columnIndex) {
checkValidState();
@@ -267,6 +279,30 @@ public List getLongList(String columnName) {
return delegate.get().getLongList(columnName);
}
+ @Override
+ public float[] getFloatArray(int columnIndex) {
+ checkValidState();
+ return delegate.get().getFloatArray(columnIndex);
+ }
+
+ @Override
+ public float[] getFloatArray(String columnName) {
+ checkValidState();
+ return delegate.get().getFloatArray(columnName);
+ }
+
+ @Override
+ public List getFloatList(int columnIndex) {
+ checkValidState();
+ return delegate.get().getFloatList(columnIndex);
+ }
+
+ @Override
+ public List getFloatList(String columnName) {
+ checkValidState();
+ return delegate.get().getFloatList(columnName);
+ }
+
@Override
public double[] getDoubleArray(int columnIndex) {
checkValidState();
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcStruct.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcStruct.java
index 6be649ae7c..a6769acfad 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcStruct.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/GrpcStruct.java
@@ -17,6 +17,7 @@
package com.google.cloud.spanner;
import static com.google.cloud.spanner.AbstractResultSet.throwNotNull;
+import static com.google.cloud.spanner.AbstractResultSet.valueProtoToFloat32;
import static com.google.cloud.spanner.AbstractResultSet.valueProtoToFloat64;
import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException;
import static com.google.common.base.Preconditions.checkArgument;
@@ -24,6 +25,7 @@
import com.google.cloud.ByteArray;
import com.google.cloud.Date;
import com.google.cloud.Timestamp;
+import com.google.cloud.spanner.AbstractResultSet.Float32Array;
import com.google.cloud.spanner.AbstractResultSet.Float64Array;
import com.google.cloud.spanner.AbstractResultSet.Int64Array;
import com.google.cloud.spanner.AbstractResultSet.LazyByteArray;
@@ -83,6 +85,9 @@ private Object writeReplace() {
case FLOAT64:
builder.set(fieldName).to((Double) value);
break;
+ case FLOAT32:
+ builder.set(fieldName).to((Float) value);
+ break;
case NUMERIC:
builder.set(fieldName).to((BigDecimal) value);
break;
@@ -135,6 +140,9 @@ private Object writeReplace() {
case FLOAT64:
builder.set(fieldName).toFloat64Array((Iterable) value);
break;
+ case FLOAT32:
+ builder.set(fieldName).toFloat32Array((Iterable) value);
+ break;
case NUMERIC:
builder.set(fieldName).toNumericArray((Iterable) value);
break;
@@ -259,6 +267,8 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot
return Long.parseLong(proto.getStringValue());
case FLOAT64:
return valueProtoToFloat64(proto);
+ case FLOAT32:
+ return valueProtoToFloat32(proto);
case NUMERIC:
checkType(fieldType, proto, KindCase.STRING_VALUE);
return new BigDecimal(proto.getStringValue());
@@ -310,11 +320,13 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) {
switch (elementType.getCode()) {
case INT64:
case ENUM:
- // For int64/float64/enum types, use custom containers. These avoid wrapper object
- // creation for non-null arrays.
+ // For int64/float64/float32/enum types, use custom containers.
+ // These avoid wrapper object creation for non-null arrays.
return new Int64Array(listValue);
case FLOAT64:
return new Float64Array(listValue);
+ case FLOAT32:
+ return new Float32Array(listValue);
case BOOL:
case NUMERIC:
case PG_NUMERIC:
@@ -418,6 +430,12 @@ protected double getDoubleInternal(int columnIndex) {
return (Double) rowData.get(columnIndex);
}
+ @Override
+ protected float getFloatInternal(int columnIndex) {
+ ensureDecoded(columnIndex);
+ return (Float) rowData.get(columnIndex);
+ }
+
@Override
protected BigDecimal getBigDecimalInternal(int columnIndex) {
ensureDecoded(columnIndex);
@@ -537,6 +555,8 @@ protected Value getValueInternal(int columnIndex) {
return Value.pgNumeric(isNull ? null : getStringInternal(columnIndex));
case FLOAT64:
return Value.float64(isNull ? null : getDoubleInternal(columnIndex));
+ case FLOAT32:
+ return Value.float32(isNull ? null : getFloatInternal(columnIndex));
case STRING:
return Value.string(isNull ? null : getStringInternal(columnIndex));
case JSON:
@@ -570,6 +590,8 @@ protected Value getValueInternal(int columnIndex) {
return Value.pgNumericArray(isNull ? null : getStringListInternal(columnIndex));
case FLOAT64:
return Value.float64Array(isNull ? null : getDoubleListInternal(columnIndex));
+ case FLOAT32:
+ return Value.float32Array(isNull ? null : getFloatListInternal(columnIndex));
case STRING:
return Value.stringArray(isNull ? null : getStringListInternal(columnIndex));
case JSON:
@@ -652,6 +674,18 @@ protected Float64Array getDoubleListInternal(int columnIndex) {
return (Float64Array) rowData.get(columnIndex);
}
+ @Override
+ protected float[] getFloatArrayInternal(int columnIndex) {
+ ensureDecoded(columnIndex);
+ return getFloatListInternal(columnIndex).toPrimitiveArray(columnIndex);
+ }
+
+ @Override
+ protected Float32Array getFloatListInternal(int columnIndex) {
+ ensureDecoded(columnIndex);
+ return (Float32Array) rowData.get(columnIndex);
+ }
+
@Override
@SuppressWarnings("unchecked") // We know ARRAY produces a List.
protected List getBigDecimalListInternal(int columnIndex) {
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Mutation.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Mutation.java
index 73995a20df..6c869c549f 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Mutation.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Mutation.java
@@ -364,6 +364,8 @@ public int hashCode() {
* mutation equality to check for modifications before committing. We noticed that when NaNs where
* used the template would always indicate a modification was present, when it turned out not to
* be the case. For more information see b/206339664.
+ *
+ *
Similar change is being done while calculating `Value.hashCode()`.
*/
private boolean areValuesEqual(List values, List otherValues) {
if (values == null && otherValues == null) {
@@ -385,9 +387,19 @@ private boolean areValuesEqual(List values, List otherValues) {
}
private boolean isNaN(Value value) {
- return !value.isNull()
- && value.getType().equals(Type.float64())
- && Double.isNaN(value.getFloat64());
+ return !value.isNull() && (isFloat64NaN(value) || isFloat32NaN(value));
+ }
+
+ // Checks if the Float64 value is either a "Double" or a "Float" NaN.
+ // Refer the comment above `areValuesEqual` for more details.
+ private boolean isFloat64NaN(Value value) {
+ return value.getType().equals(Type.float64()) && Double.isNaN(value.getFloat64());
+ }
+
+ // Checks if the Float32 value is either a "Double" or a "Float" NaN.
+ // Refer the comment above `areValuesEqual` for more details.
+ private boolean isFloat32NaN(Value value) {
+ return value.getType().equals(Type.float32()) && Float.isNaN(value.getFloat32());
}
static void toProto(Iterable mutations, List out) {
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java
index a6cc7c729e..3d12cf5ad2 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java
@@ -236,6 +236,16 @@ public long getLong(String columnName) {
return getCurrentRowAsStruct().getLong(columnName);
}
+ @Override
+ public float getFloat(int columnIndex) {
+ return getCurrentRowAsStruct().getFloat(columnIndex);
+ }
+
+ @Override
+ public float getFloat(String columnName) {
+ return getCurrentRowAsStruct().getFloat(columnName);
+ }
+
@Override
public double getDouble(int columnIndex) {
return getCurrentRowAsStruct().getDouble(columnIndex);
@@ -388,6 +398,26 @@ public List getLongList(String columnName) {
return getCurrentRowAsStruct().getLongList(columnName);
}
+ @Override
+ public float[] getFloatArray(int columnIndex) {
+ return getCurrentRowAsStruct().getFloatArray(columnIndex);
+ }
+
+ @Override
+ public float[] getFloatArray(String columnName) {
+ return getCurrentRowAsStruct().getFloatArray(columnName);
+ }
+
+ @Override
+ public List getFloatList(int columnIndex) {
+ return getCurrentRowAsStruct().getFloatList(columnIndex);
+ }
+
+ @Override
+ public List getFloatList(String columnName) {
+ return getCurrentRowAsStruct().getFloatList(columnName);
+ }
+
@Override
public double[] getDoubleArray(int columnIndex) {
return getCurrentRowAsStruct().getDoubleArray(columnIndex);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java
index 40c30148d0..0e65fa7f1b 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Struct.java
@@ -27,6 +27,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Doubles;
+import com.google.common.primitives.Floats;
import com.google.common.primitives.Longs;
import com.google.protobuf.AbstractMessage;
import com.google.protobuf.ProtocolMessageEnum;
@@ -180,6 +181,11 @@ protected long getLongInternal(int columnIndex) {
return values.get(columnIndex).getInt64();
}
+ @Override
+ protected float getFloatInternal(int columnIndex) {
+ return values.get(columnIndex).getFloat32();
+ }
+
@Override
protected double getDoubleInternal(int columnIndex) {
return values.get(columnIndex).getFloat64();
@@ -261,6 +267,16 @@ protected List getLongListInternal(int columnIndex) {
return values.get(columnIndex).getInt64Array();
}
+ @Override
+ protected float[] getFloatArrayInternal(int columnIndex) {
+ return Floats.toArray(getFloatListInternal(columnIndex));
+ }
+
+ @Override
+ protected List getFloatListInternal(int columnIndex) {
+ return values.get(columnIndex).getFloat32Array();
+ }
+
@Override
protected double[] getDoubleArrayInternal(int columnIndex) {
return Doubles.toArray(getDoubleListInternal(columnIndex));
@@ -382,6 +398,8 @@ private Object getAsObject(int columnIndex) {
case INT64:
case ENUM:
return getLongInternal(columnIndex);
+ case FLOAT32:
+ return getFloatInternal(columnIndex);
case FLOAT64:
return getDoubleInternal(columnIndex);
case NUMERIC:
@@ -410,6 +428,8 @@ private Object getAsObject(int columnIndex) {
case INT64:
case ENUM:
return getLongListInternal(columnIndex);
+ case FLOAT32:
+ return getFloatListInternal(columnIndex);
case FLOAT64:
return getDoubleListInternal(columnIndex);
case NUMERIC:
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java
index fd8cb77f39..f9967db045 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/StructReader.java
@@ -123,6 +123,22 @@ public interface StructReader {
*/
long getLong(String columnName);
+ /**
+ * @param columnIndex index of the column
+ * @return the value of a non-{@code NULL} column with type {@link Type#float32()}.
+ */
+ default float getFloat(int columnIndex) {
+ throw new UnsupportedOperationException("method should be overwritten");
+ }
+
+ /**
+ * @param columnName name of the column
+ * @return the value of a non-{@code NULL} column with type {@link Type#float32()}.
+ */
+ default float getFloat(String columnName) {
+ throw new UnsupportedOperationException("method should be overwritten");
+ }
+
/**
* @param columnIndex index of the column
* @return the value of a non-{@code NULL} column with type {@link Type#float64()}.
@@ -361,6 +377,44 @@ default Value getValue(String columnName) {
*/
List getLongList(String columnName);
+ /**
+ * @param columnIndex index of the column
+ * @return the value of a non-{@code NULL} column with type {@code Type.array(Type.float32())}.
+ * @throws NullPointerException if any element of the array value is {@code NULL}. If the array
+ * may contain {@code NULL} values, use {@link #getFloatList(int)} instead.
+ */
+ default float[] getFloatArray(int columnIndex) {
+ throw new UnsupportedOperationException("method should be overwritten");
+ }
+
+ /**
+ * @param columnName name of the column
+ * @return the value of a non-{@code NULL} column with type {@code Type.array(Type.float32())}.
+ * @throws NullPointerException if any element of the array value is {@code NULL}. If the array
+ * may contain {@code NULL} values, use {@link #getFloatList(String)} instead.
+ */
+ default float[] getFloatArray(String columnName) {
+ throw new UnsupportedOperationException("method should be overwritten");
+ }
+
+ /**
+ * @param columnIndex index of the column
+ * @return the value of a non-{@code NULL} column with type {@code Type.array(Type.float32())} The
+ * list returned by this method is lazily constructed. Create a copy of it if you intend to
+ * access each element in the list multiple times.
+ */
+ default List getFloatList(int columnIndex) {
+ throw new UnsupportedOperationException("method should be overwritten");
+ }
+
+ /**
+ * @param columnName name of the column
+ * @return the value of a non-{@code NULL} column with type {@code Type.array(Type.float32())} The
+ * list returned by this method is lazily constructed. Create a copy of it if you intend to
+ * access each element in the list multiple times.
+ */
+ List getFloatList(String columnName);
+
/**
* @param columnIndex index of the column
* @return the value of a non-{@code NULL} column with type {@code Type.array(Type.float64())}.
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Type.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Type.java
index 348db5d04a..5d871227f5 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Type.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Type.java
@@ -48,6 +48,7 @@
public final class Type implements Serializable {
private static final Type TYPE_BOOL = new Type(Code.BOOL, null, null);
private static final Type TYPE_INT64 = new Type(Code.INT64, null, null);
+ private static final Type TYPE_FLOAT32 = new Type(Code.FLOAT32, null, null);
private static final Type TYPE_FLOAT64 = new Type(Code.FLOAT64, null, null);
private static final Type TYPE_NUMERIC = new Type(Code.NUMERIC, null, null);
private static final Type TYPE_PG_NUMERIC = new Type(Code.PG_NUMERIC, null, null);
@@ -59,6 +60,7 @@ public final class Type implements Serializable {
private static final Type TYPE_DATE = new Type(Code.DATE, null, null);
private static final Type TYPE_ARRAY_BOOL = new Type(Code.ARRAY, TYPE_BOOL, null);
private static final Type TYPE_ARRAY_INT64 = new Type(Code.ARRAY, TYPE_INT64, null);
+ private static final Type TYPE_ARRAY_FLOAT32 = new Type(Code.ARRAY, TYPE_FLOAT32, null);
private static final Type TYPE_ARRAY_FLOAT64 = new Type(Code.ARRAY, TYPE_FLOAT64, null);
private static final Type TYPE_ARRAY_NUMERIC = new Type(Code.ARRAY, TYPE_NUMERIC, null);
private static final Type TYPE_ARRAY_PG_NUMERIC = new Type(Code.ARRAY, TYPE_PG_NUMERIC, null);
@@ -89,9 +91,17 @@ public static Type int64() {
return TYPE_INT64;
}
+ /**
+ * Returns the descriptor for the {@code FLOAT32} type: a floating point type with the same value
+ * domain as a Java {@code float}.
+ */
+ public static Type float32() {
+ return TYPE_FLOAT32;
+ }
+
/**
* Returns the descriptor for the {@code FLOAT64} type: a floating point type with the same value
- * domain as a Java {code double}.
+ * domain as a Java {@code double}.
*/
public static Type float64() {
return TYPE_FLOAT64;
@@ -174,6 +184,8 @@ public static Type array(Type elementType) {
return TYPE_ARRAY_BOOL;
case INT64:
return TYPE_ARRAY_INT64;
+ case FLOAT32:
+ return TYPE_ARRAY_FLOAT32;
case FLOAT64:
return TYPE_ARRAY_FLOAT64;
case NUMERIC:
@@ -264,6 +276,7 @@ public enum Code {
NUMERIC(TypeCode.NUMERIC, "unknown"),
PG_NUMERIC(TypeCode.NUMERIC, "numeric", TypeAnnotationCode.PG_NUMERIC),
FLOAT64(TypeCode.FLOAT64, "double precision"),
+ FLOAT32(TypeCode.FLOAT32, "real"),
STRING(TypeCode.STRING, "character varying"),
JSON(TypeCode.JSON, "unknown"),
PG_JSONB(TypeCode.JSON, "jsonb", TypeAnnotationCode.PG_JSONB),
@@ -565,6 +578,8 @@ static Type fromProto(com.google.spanner.v1.Type proto) {
return bool();
case INT64:
return int64();
+ case FLOAT32:
+ return float32();
case FLOAT64:
return float64();
case NUMERIC:
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 3f0155e4a5..e4db5ff146 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
@@ -149,6 +149,20 @@ public static Value int64(long v) {
return new Int64Impl(false, v);
}
+ /**
+ * Returns a {@code FLOAT32} value.
+ *
+ * @param v the value, which may be null
+ */
+ public static Value float32(@Nullable Float v) {
+ return new Float32Impl(v == null, v == null ? 0 : v);
+ }
+
+ /** Returns a {@code FLOAT32} value. */
+ public static Value float32(float v) {
+ return new Float32Impl(false, v);
+ }
+
/**
* Returns a {@code FLOAT64} value.
*
@@ -454,6 +468,40 @@ public static Value int64Array(@Nullable Iterable v) {
return int64ArrayFactory.create(v);
}
+ /**
+ * Returns an {@code ARRAY} value.
+ *
+ * @param v the source of element values, which may be null to produce a value for which {@code
+ * isNull()} is {@code true}
+ */
+ public static Value float32Array(@Nullable float[] v) {
+ return float32Array(v, 0, v == null ? 0 : v.length);
+ }
+
+ /**
+ * Returns an {@code ARRAY} value that takes its elements from a region of an array.
+ *
+ * @param v the source of element values, which may be null to produce a value for which {@code
+ * isNull()} is {@code true}
+ * @param pos the start position of {@code v} to copy values from. Ignored if {@code v} is {@code
+ * null}.
+ * @param length the number of values to copy from {@code v}. Ignored if {@code v} is {@code
+ * null}.
+ */
+ public static Value float32Array(@Nullable float[] v, int pos, int length) {
+ return float32ArrayFactory.create(v, pos, length);
+ }
+
+ /**
+ * Returns an {@code ARRAY} value.
+ *
+ * @param v the source of element values. This may be {@code null} to produce a value for which
+ * {@code isNull()} is {@code true}. Individual elements may also be {@code null}.
+ */
+ public static Value float32Array(@Nullable Iterable v) {
+ return float32ArrayFactory.create(v);
+ }
+
/**
* Returns an {@code ARRAY} value.
*
@@ -729,6 +777,13 @@ private Value() {}
*/
public abstract long getInt64();
+ /**
+ * Returns the value of a {@code FLOAT32}-typed instance.
+ *
+ * @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
+ */
+ public abstract float getFloat32();
+
/**
* Returns the value of a {@code FLOAT64}-typed instance.
*
@@ -835,6 +890,14 @@ public T getProtoEnum(
*/
public abstract List getInt64Array();
+ /**
+ * Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
+ * will never be {@code null}, elements of that list may be null.
+ *
+ * @throws IllegalStateException if {@code isNull()} or the value is not of the expected type
+ */
+ public abstract List getFloat32Array();
+
/**
* Returns the value of an {@code ARRAY}-typed instance. While the returned list itself
* will never be {@code null}, elements of that list may be null.
@@ -1052,6 +1115,23 @@ Value newValue(boolean isNull, BitSet nulls, long[] values) {
return new Int64ArrayImpl(isNull, nulls, values);
}
};
+ private static final PrimitiveArrayValueFactory float32ArrayFactory =
+ new PrimitiveArrayValueFactory() {
+ @Override
+ float[] newArray(int size) {
+ return new float[size];
+ }
+
+ @Override
+ void set(float[] arr, int i, Float value) {
+ arr[i] = value;
+ }
+
+ @Override
+ Value newValue(boolean isNull, BitSet nulls, float[] values) {
+ return new Float32ArrayImpl(isNull, nulls, values);
+ }
+ };
private static final PrimitiveArrayValueFactory float64ArrayFactory =
new PrimitiveArrayValueFactory() {
@Override
@@ -1122,6 +1202,11 @@ public long getInt64() {
throw defaultGetter(Type.int64());
}
+ @Override
+ public float getFloat32() {
+ throw defaultGetter(Type.float32());
+ }
+
@Override
public double getFloat64() {
throw defaultGetter(Type.float64());
@@ -1181,6 +1266,11 @@ public List getInt64Array() {
throw defaultGetter(Type.array(Type.int64()));
}
+ @Override
+ public List getFloat32Array() {
+ throw defaultGetter(Type.array(Type.float32()));
+ }
+
@Override
public List getFloat64Array() {
throw defaultGetter(Type.array(Type.float64()));
@@ -1285,9 +1375,29 @@ public final boolean equals(Object o) {
@Override
public final int hashCode() {
- int result = Objects.hash(getType(), isNull);
+ Type typeToHash = getType();
+ int valueHash = isNull ? 0 : valueHash();
+
+ /**
+ * We are relaxing equality values here, making sure that Double.NaNs and Float.NaNs are equal
+ * to each other. This is because our Cloud Spanner Import / Export template in Apache Beam
+ * uses the mutation equality to check for modifications before committing. We noticed that
+ * when NaNs where used the template would always indicate a modification was present, when it
+ * turned out not to be the case.
+ *
+ *
With FLOAT32 being introduced, we want to ensure the backward compatibility of the NaN
+ * equality checks that existed for FLOAT64. We're promoting the type to FLOAT64 while
+ * calculating the type hash when the value is a NaN. We're doing a similar type promotion
+ * while calculating valueHash of Float32 type. Note that this is not applicable for composite
+ * types containing FLOAT32.
+ */
+ if (type.getCode() == Type.Code.FLOAT32 && !isNull && Float.isNaN(getFloat32())) {
+ typeToHash = Type.float64();
+ }
+
+ int result = Objects.hash(typeToHash, isNull);
if (!isNull) {
- result = 31 * result + valueHash();
+ result = 31 * result + valueHash;
}
return result;
}
@@ -1492,6 +1602,46 @@ int valueHash() {
}
}
+ private static class Float32Impl extends AbstractValue {
+ private final float value;
+
+ private Float32Impl(boolean isNull, float value) {
+ super(isNull, Type.float32());
+ this.value = value;
+ }
+
+ @Override
+ public float getFloat32() {
+ checkNotNull();
+ return value;
+ }
+
+ @Override
+ com.google.protobuf.Value valueToProto() {
+ return com.google.protobuf.Value.newBuilder().setNumberValue(value).build();
+ }
+
+ @Override
+ void valueToString(StringBuilder b) {
+ b.append(value);
+ }
+
+ @Override
+ boolean valueEquals(Value v) {
+ return ((Float32Impl) v).value == value;
+ }
+
+ @Override
+ int valueHash() {
+ // For backward compatibility of NaN equality checks with Float64 NaNs.
+ // Refer the comment in `Value.hashCode()` for more details.
+ if (!isNull() && Float.isNaN(value)) {
+ return Double.valueOf(Double.NaN).hashCode();
+ }
+ return Float.valueOf(value).hashCode();
+ }
+ }
+
private static class Float64Impl extends AbstractValue {
private final double value;
@@ -2106,6 +2256,46 @@ int arrayHash() {
}
}
+ private static class Float32ArrayImpl extends PrimitiveArrayImpl {
+ private final float[] values;
+
+ private Float32ArrayImpl(boolean isNull, BitSet nulls, float[] values) {
+ super(isNull, Type.float32(), nulls);
+ this.values = values;
+ }
+
+ @Override
+ public List getFloat32Array() {
+ return getArray();
+ }
+
+ @Override
+ boolean valueEquals(Value v) {
+ Float32ArrayImpl that = (Float32ArrayImpl) v;
+ return Arrays.equals(values, that.values);
+ }
+
+ @Override
+ int size() {
+ return values.length;
+ }
+
+ @Override
+ Float getValue(int i) {
+ return values[i];
+ }
+
+ @Override
+ com.google.protobuf.Value getValueAsProto(int i) {
+ return com.google.protobuf.Value.newBuilder().setNumberValue(values[i]).build();
+ }
+
+ @Override
+ int arrayHash() {
+ return Arrays.hashCode(values);
+ }
+ }
+
private static class Float64ArrayImpl extends PrimitiveArrayImpl {
private final double[] values;
@@ -2588,6 +2778,8 @@ private Value getValue(int fieldIndex) {
return Value.pgJsonb(value.getPgJsonb(fieldIndex));
case BYTES:
return Value.bytes(value.getBytes(fieldIndex));
+ case FLOAT32:
+ return Value.float32(value.getFloat(fieldIndex));
case FLOAT64:
return Value.float64(value.getDouble(fieldIndex));
case NUMERIC:
@@ -2622,6 +2814,8 @@ private Value getValue(int fieldIndex) {
case BYTES:
case PROTO:
return Value.bytesArray(value.getBytesList(fieldIndex));
+ case FLOAT32:
+ return Value.float32Array(value.getFloatList(fieldIndex));
case FLOAT64:
return Value.float64Array(value.getDoubleList(fieldIndex));
case NUMERIC:
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ValueBinder.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ValueBinder.java
index 9915e12175..d675686ffe 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ValueBinder.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/ValueBinder.java
@@ -81,6 +81,16 @@ public R to(@Nullable Long value) {
return handle(Value.int64(value));
}
+ /** Binds to {@code Value.float32(value)} */
+ public R to(float value) {
+ return handle(Value.float32(value));
+ }
+
+ /** Binds to {@code Value.float32(value)} */
+ public R to(@Nullable Float value) {
+ return handle(Value.float32(value));
+ }
+
/** Binds to {@code Value.float64(value)} */
public R to(double value) {
return handle(Value.float64(value));
@@ -198,6 +208,21 @@ public R toInt64Array(@Nullable Iterable values) {
return handle(Value.int64Array(values));
}
+ /** Binds to {@code Value.float32Array(values)} */
+ public R toFloat32Array(@Nullable float[] values) {
+ return handle(Value.float32Array(values));
+ }
+
+ /** Binds to {@code Value.float32Array(values, pos, length)} */
+ public R toFloat32Array(@Nullable float[] values, int pos, int length) {
+ return handle(Value.float32Array(values, pos, length));
+ }
+
+ /** Binds to {@code Value.float32Array(values)} */
+ public R toFloat32Array(@Nullable Iterable values) {
+ return handle(Value.float32Array(values));
+ }
+
/** Binds to {@code Value.float64Array(values)} */
public R toFloat64Array(@Nullable double[] values) {
return handle(Value.float64Array(values));
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java
index 1b15ec5082..b5e4060ddd 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DirectExecuteResultSet.java
@@ -180,6 +180,12 @@ public long getLong(String columnName) {
return delegate.getLong(columnName);
}
+ @Override
+ public float getFloat(int columnIndex) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getFloat(columnIndex);
+ }
+
@Override
public double getDouble(int columnIndex) {
Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
@@ -198,6 +204,12 @@ public BigDecimal getBigDecimal(int columnIndex) {
return delegate.getBigDecimal(columnIndex);
}
+ @Override
+ public float getFloat(String columnName) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getFloat(columnName);
+ }
+
@Override
public double getDouble(String columnName) {
Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
@@ -336,6 +348,30 @@ public List getLongList(String columnName) {
return delegate.getLongList(columnName);
}
+ @Override
+ public float[] getFloatArray(int columnIndex) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getFloatArray(columnIndex);
+ }
+
+ @Override
+ public float[] getFloatArray(String columnName) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getFloatArray(columnName);
+ }
+
+ @Override
+ public List getFloatList(int columnIndex) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getFloatList(columnIndex);
+ }
+
+ @Override
+ public List getFloatList(String columnName) {
+ Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
+ return delegate.getFloatList(columnName);
+ }
+
@Override
public double[] getDoubleArray(int columnIndex) {
Preconditions.checkState(nextCalledByClient, MISSING_NEXT_CALL);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java
index a8de14e512..bd7c794a0f 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReplaceableForwardingResultSet.java
@@ -189,6 +189,18 @@ public long getLong(String columnName) {
return delegate.getLong(columnName);
}
+ @Override
+ public float getFloat(int columnIndex) {
+ checkClosed();
+ return delegate.getFloat(columnIndex);
+ }
+
+ @Override
+ public float getFloat(String columnName) {
+ checkClosed();
+ return delegate.getFloat(columnName);
+ }
+
@Override
public double getDouble(int columnIndex) {
checkClosed();
@@ -345,6 +357,30 @@ public List getLongList(String columnName) {
return delegate.getLongList(columnName);
}
+ @Override
+ public float[] getFloatArray(int columnIndex) {
+ checkClosed();
+ return delegate.getFloatArray(columnIndex);
+ }
+
+ @Override
+ public float[] getFloatArray(String columnName) {
+ checkClosed();
+ return delegate.getFloatArray(columnName);
+ }
+
+ @Override
+ public List getFloatList(int columnIndex) {
+ checkClosed();
+ return delegate.getFloatList(columnIndex);
+ }
+
+ @Override
+ public List getFloatList(String columnName) {
+ checkClosed();
+ return delegate.getFloatList(columnName);
+ }
+
@Override
public double[] getDoubleArray(int columnIndex) {
checkClosed();
diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java
index 4fc3c67ceb..16dd51a36a 100644
--- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java
+++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractStructReaderTypesTest.java
@@ -58,6 +58,11 @@ protected long getLongInternal(int columnIndex) {
return 0;
}
+ @Override
+ protected float getFloatInternal(int columnIndex) {
+ return 0f;
+ }
+
@Override
protected double getDoubleInternal(int columnIndex) {
return 0;
@@ -134,6 +139,16 @@ protected List getLongListInternal(int columnIndex) {
return null;
}
+ @Override
+ protected float[] getFloatArrayInternal(int columnIndex) {
+ return null;
+ }
+
+ @Override
+ protected List getFloatListInternal(int columnIndex) {
+ return null;
+ }
+
@Override
protected double[] getDoubleArrayInternal(int columnIndex) {
return null;
@@ -222,6 +237,13 @@ public static Collection