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.0 com.google.cloud google-cloud-spanner-bom - 6.60.1 + 6.60.2-SNAPSHOT pom com.google.cloud @@ -53,43 +53,43 @@ com.google.cloud google-cloud-spanner - 6.60.1 + 6.60.2-SNAPSHOT com.google.cloud google-cloud-spanner test-jar - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc proto-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.0 com.google.cloud google-cloud-spanner-executor - 6.60.1 + 6.60.2-SNAPSHOT jar Google Cloud Spanner Executor com.google.cloud google-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.0 com.google.cloud google-cloud-spanner - 6.60.1 + 6.60.2-SNAPSHOT jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 6.60.1 + 6.60.2-SNAPSHOT google-cloud-spanner diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index 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.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.60.1 + 6.60.2-SNAPSHOT grpc-google-cloud-spanner-admin-database-v1 GRPC library for grpc-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.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.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.60.1 + 6.60.2-SNAPSHOT grpc-google-cloud-spanner-admin-instance-v1 GRPC library for grpc-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.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.0 com.google.api.grpc grpc-google-cloud-spanner-executor-v1 - 6.60.1 + 6.60.2-SNAPSHOT grpc-google-cloud-spanner-executor-v1 GRPC library for google-cloud-spanner com.google.cloud google-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.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.60.1 + 6.60.2-SNAPSHOT grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-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.cloud google-cloud-spanner-parent pom - 6.60.1 + 6.60.2-SNAPSHOT Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -61,47 +61,47 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-executor-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-executor-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.60.1 + 6.60.2-SNAPSHOT com.google.cloud google-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.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.60.1 + 6.60.2-SNAPSHOT proto-google-cloud-spanner-admin-database-v1 PROTO library for proto-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.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.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.60.1 + 6.60.2-SNAPSHOT proto-google-cloud-spanner-admin-instance-v1 PROTO library for proto-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.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.0 com.google.api.grpc proto-google-cloud-spanner-executor-v1 - 6.60.1 + 6.60.2-SNAPSHOT proto-google-cloud-spanner-executor-v1 Proto library for google-cloud-spanner com.google.cloud google-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.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.60.1 + 6.60.2-SNAPSHOT proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-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.cloud google-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.cloud google-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.cloud google-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-test nam11 us-east1 - cmek-test-key-ring - cmek-test-key + java-client-integration-test-cmek-ring + java-client-integration-test-cmek-key mysample quick-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-test nam11 us-east1 - cmek-test-key-ring - cmek-test-key + java-client-integration-test-cmek-ring + java-client-integration-test-cmek-key mysample mysample-instance quick-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-test nam11 us-east1 - cmek-test-key-ring - cmek-test-key + java-client-integration-test-cmek-ring + java-client-integration-test-cmek-key mysample quick-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 @@ 7012 com/google/cloud/spanner/connection/Connection void 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 parameters() { Collections.singletonList("getValue") }, {Type.int64(), "getLongInternal", 123L, "getLong", Collections.singletonList("getValue")}, + { + Type.float32(), + "getFloatInternal", + 2.0f, + "getFloat", + Collections.singletonList("getValue") + }, { Type.float64(), "getDoubleInternal", @@ -306,6 +328,20 @@ public static Collection parameters() { "getLongList", Arrays.asList("getLongArray", "getValue") }, + { + Type.array(Type.float32()), + "getFloatArrayInternal", + new float[] {1.0f, 2.0f}, + "getFloatArray", + Arrays.asList("getFloatList", "getValue") + }, + { + Type.array(Type.float32()), + "getFloatListInternal", + Arrays.asList(2.0f, 4.0f), + "getFloatList", + Arrays.asList("getFloatArray", "getValue") + }, { Type.array(Type.float64()), "getDoubleArrayInternal", diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java index cb73618d99..5e6a4ffc2c 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/GrpcResultSetTest.java @@ -537,6 +537,8 @@ public void serialization() { Value.int64(null), Value.float64(1.0), Value.float64(null), + Value.float32(1.0f), + Value.float32(null), Value.bytes(ByteArray.fromBase64("abcd")), Value.bytesFromBase64( Base64.getEncoder().encodeToString("test".getBytes(StandardCharsets.UTF_8))), @@ -554,6 +556,8 @@ public void serialization() { Value.int64Array((long[]) null), Value.float64Array(new double[] {1.1, 2.2, 3.3}), Value.float64Array((double[]) null), + Value.float32Array(new float[] {1.1f, 2.2f, 3.3f}), + Value.float32Array((float[]) null), Value.bytesArray(Arrays.asList(ByteArray.fromBase64("abcd"), null)), Value.bytesArrayFromBase64( Arrays.asList( @@ -655,6 +659,22 @@ public void getDouble() { assertThat(resultSet.getDouble(0)).isWithin(0.0).of(Double.MAX_VALUE); } + @Test + public void getFloat() { + consumer.onPartialResultSet( + PartialResultSet.newBuilder() + .setMetadata(makeMetadata(Type.struct(Type.StructField.of("f", Type.float32())))) + .addValues(Value.float32(Float.MIN_VALUE).toProto()) + .addValues(Value.float32(Float.MAX_VALUE).toProto()) + .build()); + consumer.onCompleted(); + + assertThat(resultSet.next()).isTrue(); + assertThat(resultSet.getFloat(0)).isWithin(0.0f).of(Float.MIN_VALUE); + assertThat(resultSet.next()).isTrue(); + assertThat(resultSet.getFloat(0)).isWithin(0.0f).of(Float.MAX_VALUE); + } + @Test public void getBigDecimal() { consumer.onPartialResultSet( @@ -877,6 +897,25 @@ public void getDoubleArray() { .inOrder(); } + @Test + public void getFloatArray() { + float[] floatArray = {Float.MAX_VALUE, Float.MIN_VALUE, 111, 333, 444, 0, -1, -2234}; + + consumer.onPartialResultSet( + PartialResultSet.newBuilder() + .setMetadata( + makeMetadata(Type.struct(Type.StructField.of("f", Type.array(Type.float32()))))) + .addValues(Value.float32Array(floatArray).toProto()) + .build()); + consumer.onCompleted(); + + assertThat(resultSet.next()).isTrue(); + assertThat(resultSet.getFloatArray(0)) + .usingTolerance(0.0) + .containsExactly(floatArray) + .inOrder(); + } + @Test public void getBigDecimalList() { List bigDecimalsList = new ArrayList<>(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java index f27aa405aa..3b0b7812d2 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java @@ -1284,6 +1284,8 @@ private Statement buildStatement( case DATE: builder.bind(fieldName).toDateArray(null); break; + case FLOAT32: + builder.bind(fieldName).toFloat32Array((Iterable) null); case FLOAT64: builder.bind(fieldName).toFloat64Array((Iterable) null); break; @@ -1327,6 +1329,8 @@ private Statement buildStatement( case DATE: builder.bind(fieldName).to((Date) null); break; + case FLOAT32: + builder.bind(fieldName).to((Float) null); case FLOAT64: builder.bind(fieldName).to((Double) null); break; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java index f38b5e47b8..0cfb57d4a2 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MutationTest.java @@ -211,12 +211,37 @@ public void equalsAndHashCode() { Mutation.delete("T1", KeySet.singleKey(Key.of("k"))), Mutation.delete("T1", Key.of("k"))); // Test NaNs + // Refer the comment in `Value.hashCode()` for more details on NaN equality. tester.addEqualityGroup( Mutation.newInsertBuilder("T1").set("C").to(Double.NaN).build(), Mutation.newInsertBuilder("T1").set("C").to(Value.float64(Double.NaN)).build(), Mutation.newInsertBuilder("T1").set("C").to(Float.NaN).build(), - Mutation.newInsertBuilder("T1").set("C").to(Value.float64(Float.NaN)).build()); + Mutation.newInsertBuilder("T1").set("C").to(Value.float64(Float.NaN)).build(), + Mutation.newInsertBuilder("T1").set("C").to(Value.float32(Float.NaN)).build()); + // Test NaN arrays + tester.addEqualityGroup( + Mutation.newInsertBuilder("T1").set("C").toFloat32Array(new float[] {Float.NaN}).build(), + Mutation.newInsertBuilder("T1") + .set("C") + .toFloat32Array(new float[] {Float.NaN}, 0, 1) + .build(), + Mutation.newInsertBuilder("T1") + .set("C") + .toFloat32Array(Collections.singletonList(Float.NaN)) + .build(), + Mutation.newInsertBuilder("T1") + .set("C") + .to(Value.float32Array(new float[] {Float.NaN})) + .build(), + Mutation.newInsertBuilder("T1") + .set("C") + .to(Value.float32Array(new float[] {Float.NaN}, 0, 1)) + .build(), + Mutation.newInsertBuilder("T1") + .set("C") + .to(Value.float32Array(Collections.singletonList(Float.NaN))) + .build()); tester.addEqualityGroup( Mutation.newInsertBuilder("T1").set("C").toFloat64Array(new double[] {Double.NaN}).build(), Mutation.newInsertBuilder("T1").set("C").toFloat64Array(new double[] {Float.NaN}).build(), @@ -270,6 +295,11 @@ public void equalsAndHashCode() { .set("C") .toFloat64Array(Arrays.asList(null, (double) Float.NaN)) .build()); + tester.addEqualityGroup( + Mutation.newInsertBuilder("T1") + .set("C") + .toFloat32Array(Arrays.asList(null, Float.NaN)) + .build()); tester.testEquals(); } @@ -523,11 +553,17 @@ private Mutation.WriteBuilder appendAllTypes(Mutation.WriteBuilder builder) { .to((Long) null) .set("intValue") .to(Value.int64(1L)) - .set("float") + .set("float32") + .to(42.1f) + .set("float32Null") + .to((Float) null) + .set("float32Value") + .to(Value.float32(10f)) + .set("float64") .to(42.1) - .set("floatNull") + .set("float64Null") .to((Double) null) - .set("floatValue") + .set("float64Value") .to(Value.float64(10D)) .set("string") .to("str") @@ -583,11 +619,17 @@ private Mutation.WriteBuilder appendAllTypes(Mutation.WriteBuilder builder) { .toInt64Array((long[]) null) .set("intArrValue") .to(Value.int64Array(ImmutableList.of(1L, 2L))) - .set("floatArr") + .set("float32Arr") + .toFloat32Array(new float[] {1.1f, 2.2f, 3.3f}) + .set("float32ArrNull") + .toFloat32Array((float[]) null) + .set("float32ArrValue") + .to(Value.float32Array(ImmutableList.of(10.1F, 10.2F, 10.3F))) + .set("float64Arr") .toFloat64Array(new double[] {1.1, 2.2, 3.3}) - .set("floatArrNull") + .set("float64ArrNull") .toFloat64Array((double[]) null) - .set("floatArrValue") + .set("float64ArrValue") .to(Value.float64Array(ImmutableList.of(10.1D, 10.2D, 10.3D))) .set("stringArr") .toStringArray(ImmutableList.of("one", "two")) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RandomResultSetGenerator.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RandomResultSetGenerator.java index b5bbb9dd49..6cf1d0900a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RandomResultSetGenerator.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RandomResultSetGenerator.java @@ -37,6 +37,7 @@ public class RandomResultSetGenerator { Type.newBuilder().setCode(TypeCode.BOOL).build(), Type.newBuilder().setCode(TypeCode.INT64).build(), Type.newBuilder().setCode(TypeCode.FLOAT64).build(), + Type.newBuilder().setCode(TypeCode.FLOAT32).build(), Type.newBuilder().setCode(TypeCode.STRING).build(), Type.newBuilder().setCode(TypeCode.BYTES).build(), Type.newBuilder().setCode(TypeCode.DATE).build(), @@ -53,6 +54,10 @@ public class RandomResultSetGenerator { .setCode(TypeCode.ARRAY) .setArrayElementType(Type.newBuilder().setCode(TypeCode.FLOAT64)) .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.FLOAT32)) + .build(), Type.newBuilder() .setCode(TypeCode.ARRAY) .setArrayElementType(Type.newBuilder().setCode(TypeCode.STRING)) @@ -138,6 +143,9 @@ private void setRandomValue(Value.Builder builder, Type type) { case FLOAT64: builder.setNumberValue(random.nextDouble()); break; + case FLOAT32: + builder.setNumberValue(random.nextFloat()); + break; case INT64: builder.setStringValue(String.valueOf(random.nextLong())); break; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ReadAsyncTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ReadAsyncTest.java index bb8d130914..6801c82b66 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ReadAsyncTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ReadAsyncTest.java @@ -298,9 +298,12 @@ public void readOnlyTransaction() throws Exception { values2 = rs.toListAsync(input -> input.getString("Value"), executor); } } + + ApiFuture>> allValuesAsList = + ApiFutures.allAsList(Arrays.asList(values1, values2)); ApiFuture> allValues = ApiFutures.transform( - ApiFutures.allAsList(Arrays.asList(values1, values2)), + allValuesAsList, input -> Iterables.mergeSorted( input, diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ReadFormatTestRunner.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ReadFormatTestRunner.java index a72c9872fa..8d97d9d894 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ReadFormatTestRunner.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ReadFormatTestRunner.java @@ -170,6 +170,9 @@ private void assertRow(Struct actualRow, JSONArray expectedRow) throws Exception case INT64: assertThat(actualRow.getLong(i)).isEqualTo(expectedRow.getLong(i)); break; + case FLOAT32: + assertThat(actualRow.getFloat(i)).isEqualTo(expectedRow.getFloat(i)); + break; case FLOAT64: assertThat(actualRow.getDouble(i)).isEqualTo(expectedRow.getDouble(i)); break; @@ -208,6 +211,9 @@ private List getRawList(Struct actualRow, int index, Type elementType) { case INT64: rawList = actualRow.getLongList(index); break; + case FLOAT32: + rawList = actualRow.getFloatList(index); + break; case FLOAT64: rawList = actualRow.getDoubleList(index); break; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java index 8e1f257594..454bd3c70a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ResultSetsTest.java @@ -31,6 +31,7 @@ import com.google.cloud.spanner.SingerProto.Genre; import com.google.cloud.spanner.SingerProto.SingerInfo; import com.google.common.primitives.Doubles; +import com.google.common.primitives.Floats; import com.google.common.primitives.Longs; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.AbstractMessage; @@ -53,6 +54,7 @@ public class ResultSetsTest { @Test public void resultSetIteration() { double doubleVal = 1.2; + float floatVal = 6.626f; BigDecimal bigDecimalVal = BigDecimal.valueOf(123, 2); String stringVal = "stringVal"; String jsonVal = "{\"color\":\"red\",\"value\":\"#f00\"}"; @@ -71,6 +73,7 @@ public void resultSetIteration() { boolean[] boolArray = {true, false, true, true, false}; long[] longArray = {Long.MAX_VALUE, Long.MIN_VALUE, 0, 1, -1}; double[] doubleArray = {Double.MIN_VALUE, Double.MAX_VALUE, 0, 1, -1, 1.2341}; + float[] floatArray = {Float.MIN_VALUE, Float.MAX_VALUE, 0, 1, -1, 1.2341f}; BigDecimal[] bigDecimalArray = { BigDecimal.valueOf(1, Integer.MAX_VALUE), BigDecimal.valueOf(1, Integer.MIN_VALUE), @@ -102,6 +105,7 @@ public void resultSetIteration() { Type.StructField.of("f2", Type.int64()), Type.StructField.of("f3", Type.bool()), Type.StructField.of("doubleVal", Type.float64()), + Type.StructField.of("floatVal", Type.float32()), Type.StructField.of("bigDecimalVal", Type.numeric()), Type.StructField.of("stringVal", Type.string()), Type.StructField.of("jsonVal", Type.json()), @@ -116,6 +120,7 @@ public void resultSetIteration() { Type.StructField.of("boolArray", Type.array(Type.bool())), Type.StructField.of("longArray", Type.array(Type.int64())), Type.StructField.of("doubleArray", Type.array(Type.float64())), + Type.StructField.of("floatArray", Type.array(Type.float32())), Type.StructField.of("bigDecimalArray", Type.array(Type.numeric())), Type.StructField.of("byteArray", Type.array(Type.bytes())), Type.StructField.of("timestampArray", Type.array(Type.timestamp())), @@ -138,6 +143,8 @@ public void resultSetIteration() { .to(Value.bool(true)) .set("doubleVal") .to(Value.float64(doubleVal)) + .set("floatVal") + .to(Value.float32(floatVal)) .set("bigDecimalVal") .to(Value.numeric(bigDecimalVal)) .set("stringVal") @@ -162,6 +169,8 @@ public void resultSetIteration() { .to(Value.int64Array(longArray)) .set("doubleArray") .to(Value.float64Array(doubleArray)) + .set("floatArray") + .to(Value.float32Array(floatArray)) .set("bigDecimalArray") .to(Value.numericArray(Arrays.asList(bigDecimalArray))) .set("byteArray") @@ -195,6 +204,8 @@ public void resultSetIteration() { .to(Value.bool(null)) .set("doubleVal") .to(Value.float64(doubleVal)) + .set("floatVal") + .to(Value.float32(floatVal)) .set("bigDecimalVal") .to(Value.numeric(bigDecimalVal)) .set("stringVal") @@ -219,6 +230,8 @@ public void resultSetIteration() { .to(Value.int64Array(longArray)) .set("doubleArray") .to(Value.float64Array(doubleArray)) + .set("floatArray") + .to(Value.float32Array(floatArray)) .set("bigDecimalArray") .to(Value.numericArray(Arrays.asList(bigDecimalArray))) .set("byteArray") @@ -274,6 +287,10 @@ public void resultSetIteration() { assertThat(rs.getValue("doubleVal").getFloat64()).isWithin(0.0).of(doubleVal); assertThat(rs.getDouble(columnIndex)).isWithin(0.0).of(doubleVal); assertThat(rs.getValue(columnIndex++).getFloat64()).isWithin(0.0).of(doubleVal); + assertThat(rs.getFloat(columnIndex)).isWithin(0.0f).of(floatVal); + assertThat(rs.getValue(columnIndex++).getFloat32()).isWithin(0.0f).of(floatVal); + assertThat(rs.getFloat("floatVal")).isWithin(0.0f).of(floatVal); + assertThat(rs.getValue("floatVal").getFloat32()).isWithin(0.0f).of(floatVal); assertThat(rs.getBigDecimal("bigDecimalVal")).isEqualTo(new BigDecimal("1.23")); assertThat(rs.getValue("bigDecimalVal")).isEqualTo(Value.numeric(new BigDecimal("1.23"))); assertThat(rs.getBigDecimal(columnIndex)).isEqualTo(new BigDecimal("1.23")); @@ -338,6 +355,17 @@ public void resultSetIteration() { assertThat(rs.getValue("doubleArray")).isEqualTo(Value.float64Array(doubleArray)); assertThat(rs.getDoubleList(columnIndex++)).isEqualTo(Doubles.asList(doubleArray)); assertThat(rs.getDoubleList("doubleArray")).isEqualTo(Doubles.asList(doubleArray)); + + assertThat(rs.getFloatArray(columnIndex)).usingTolerance(0.0f).containsAtLeast(floatArray); + assertThat(rs.getValue(columnIndex)).isEqualTo(Value.float32Array(floatArray)); + assertThat(rs.getFloatArray("floatArray")) + .usingTolerance(0.0f) + .containsExactly(floatArray) + .inOrder(); + assertThat(rs.getValue("floatArray")).isEqualTo(Value.float32Array(floatArray)); + assertThat(rs.getFloatList(columnIndex++)).isEqualTo(Floats.asList(floatArray)); + assertThat(rs.getFloatList("floatArray")).isEqualTo(Floats.asList(floatArray)); + assertThat(rs.getBigDecimalList(columnIndex)).isEqualTo(Arrays.asList(bigDecimalArray)); assertThat(rs.getValue(columnIndex++)) .isEqualTo(Value.numericArray(Arrays.asList(bigDecimalArray))); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TypeTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TypeTest.java index 11b708ed48..6eedc058c5 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TypeTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TypeTest.java @@ -110,6 +110,16 @@ Type newType() { }.test(); } + @Test + public void float32() { + new ScalarTypeTester(Type.Code.FLOAT32, TypeCode.FLOAT32) { + @Override + Type newType() { + return Type.float32(); + } + }.test(); + } + @Test public void float64() { new ScalarTypeTester(Type.Code.FLOAT64, TypeCode.FLOAT64) { @@ -307,6 +317,16 @@ Type newElementType() { }.test(); } + @Test + public void float32Array() { + new ArrayTypeTester(Type.Code.FLOAT32, TypeCode.FLOAT32, true) { + @Override + Type newElementType() { + return Type.float32(); + } + }.test(); + } + @Test public void float64Array() { new ArrayTypeTester(Type.Code.FLOAT64, TypeCode.FLOAT64, true) { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java index d50814e84d..204880bf7d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueBinderTest.java @@ -265,6 +265,14 @@ public static Long defaultLongWrapper() { return 1234L; } + public static float defaultFloatPrimitive() { + return 1.0f; + } + + public static Float defaultFloatWrapper() { + return 1.0f; + } + public static double defaultDoublePrimitive() { return 1.0; } @@ -329,6 +337,14 @@ public static Iterable defaultLongIterable() { return Arrays.asList(1L, 2L); } + public static float[] defaultFloatArray() { + return new float[] {1.0f, 2.0f}; + } + + public static Iterable defaultFloatIterable() { + return Arrays.asList(1.0f, 2.0f); + } + public static double[] defaultDoubleArray() { return new double[] {1.0, 2.0}; } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java index 5176013cf3..d692fc44e7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java @@ -185,6 +185,37 @@ public void int64WrapperNull() { assertEquals("NULL", v.getAsString()); } + @Test + public void float32() { + Value v = Value.float32(1.23f); + assertThat(v.getType()).isEqualTo(Type.float32()); + assertThat(v.isNull()).isFalse(); + assertThat(v.getFloat32()).isWithin(0.0001f).of(1.23f); + assertThat(v.toString()).isEqualTo("1.23"); + assertEquals("1.23", v.getAsString()); + } + + @Test + public void float32Wrapper() { + Value v = Value.float32(Float.valueOf(1.23f)); + assertThat(v.getType()).isEqualTo(Type.float32()); + assertThat(v.isNull()).isFalse(); + assertThat(v.getFloat32()).isWithin(0.0001f).of(1.23f); + assertThat(v.toString()).isEqualTo("1.23"); + assertEquals("1.23", v.getAsString()); + } + + @Test + public void float32WrapperNull() { + Value v = Value.float32(null); + assertThat(v.getType()).isEqualTo(Type.float32()); + assertThat(v.isNull()).isTrue(); + assertThat(v.toString()).isEqualTo(NULL_STRING); + IllegalStateException e = assertThrows(IllegalStateException.class, v::getFloat32); + assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); + } + @Test public void float64() { Value v = Value.float64(1.23); @@ -863,6 +894,60 @@ public void int64ArrayNullTryGetBool() { assertThat(e.getMessage()).contains("Expected: BOOL actual: ARRAY"); } + @Test + public void float32Array() { + Value v = Value.float32Array(new float[] {.1f, .2f}); + assertThat(v.isNull()).isFalse(); + assertThat(v.getFloat32Array()).containsExactly(.1f, .2f).inOrder(); + assertThat(v.toString()).isEqualTo("[0.1,0.2]"); + assertEquals("[0.1,0.2]", v.getAsString()); + } + + @Test + public void float32ArrayRange() { + Value v = Value.float32Array(new float[] {.1f, .2f, .3f, .4f, .5f}, 1, 3); + assertThat(v.isNull()).isFalse(); + assertThat(v.getFloat32Array()).containsExactly(.2f, .3f, .4f).inOrder(); + assertThat(v.toString()).isEqualTo("[0.2,0.3,0.4]"); + assertEquals("[0.2,0.3,0.4]", v.getAsString()); + } + + @Test + public void float32ArrayNull() { + Value v = Value.float32Array((float[]) null); + assertThat(v.isNull()).isTrue(); + assertThat(v.toString()).isEqualTo(NULL_STRING); + IllegalStateException e = assertThrows(IllegalStateException.class, v::getFloat32Array); + assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); + } + + @Test + public void float32ArrayWrapper() { + Value v = Value.float32Array(Arrays.asList(.1f, null, .3f)); + assertThat(v.isNull()).isFalse(); + assertThat(v.getFloat32Array()).containsExactly(.1f, null, .3f).inOrder(); + assertThat(v.toString()).isEqualTo("[0.1,NULL,0.3]"); + assertEquals("[0.1,NULL,0.3]", v.getAsString()); + } + + @Test + public void float32ArrayWrapperNull() { + Value v = Value.float32Array((Iterable) null); + assertThat(v.isNull()).isTrue(); + assertThat(v.toString()).isEqualTo(NULL_STRING); + IllegalStateException e = assertThrows(IllegalStateException.class, v::getFloat32Array); + assertThat(e.getMessage()).contains("null value"); + assertEquals("NULL", v.getAsString()); + } + + @Test + public void float32ArrayTryGetFloat64Array() { + Value value = Value.float32Array(Collections.singletonList(.1f)); + IllegalStateException e = assertThrows(IllegalStateException.class, value::getFloat64Array); + assertThat(e.getMessage()).contains("Expected: ARRAY actual: ARRAY"); + } + @Test public void float64Array() { Value v = Value.float64Array(new double[] {.1, .2}); @@ -1426,6 +1511,13 @@ public void testValueToProto() { com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), Value.int64(null).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNumberValue(3.14f).build(), + Value.float32(3.14f).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(), + Value.float32(null).toProto()); + assertEquals( com.google.protobuf.Value.newBuilder().setNumberValue(3.14d).build(), Value.float64(3.14d).toProto()); @@ -1512,6 +1604,18 @@ public void testValueToProto() { .build()))) .build(), Value.int64Array(Arrays.asList(1L, null)).toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder().setNumberValue(3.14f).build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()))) + .build(), + Value.float32Array(Arrays.asList(3.14f, null)).toProto()); assertEquals( com.google.protobuf.Value.newBuilder() .setListValue( @@ -1667,6 +1771,29 @@ public void testValueToProto() { .build(), Value.struct(Struct.newBuilder().add(Value.int64Array(Arrays.asList(1L, null))).build()) .toProto()); + assertEquals( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + Arrays.asList( + com.google.protobuf.Value.newBuilder() + .setNumberValue(3.14f) + .build(), + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build())) + .build()) + .build()) + .build()) + .build(), + Value.struct( + Struct.newBuilder().add(Value.float32Array(Arrays.asList(3.14f, null))).build()) + .toProto()); assertEquals( com.google.protobuf.Value.newBuilder() .setListValue( @@ -1872,6 +1999,10 @@ public void testEqualsHashCode() { tester.addEqualityGroup(Value.int64(456)); tester.addEqualityGroup(Value.int64(null)); + tester.addEqualityGroup(Value.float32(1.23f), Value.float32(Float.valueOf(1.23f))); + tester.addEqualityGroup(Value.float32(4.56f)); + tester.addEqualityGroup(Value.float32(null)); + tester.addEqualityGroup(Value.float64(1.23), Value.float64(Double.valueOf(1.23))); tester.addEqualityGroup(Value.float64(4.56)); tester.addEqualityGroup(Value.float64(null)); @@ -1938,6 +2069,14 @@ public void testEqualsHashCode() { tester.addEqualityGroup(Value.int64Array(Collections.singletonList(3L))); tester.addEqualityGroup(Value.int64Array((Iterable) null)); + tester.addEqualityGroup( + Value.float32Array(Arrays.asList(.1f, .2f)), + Value.float32Array(new float[] {.1f, .2f}), + Value.float32Array(new float[] {.0f, .1f, .2f, .3f}, 1, 2), + Value.float32Array(plainIterable(.1f, .2f))); + tester.addEqualityGroup(Value.float32Array(Collections.singletonList(.3f))); + tester.addEqualityGroup(Value.float32Array((Iterable) null)); + tester.addEqualityGroup( Value.float64Array(Arrays.asList(.1, .2)), Value.float64Array(new double[] {.1, .2}), @@ -2009,6 +2148,11 @@ public void testGetAsString() { assertEquals(String.valueOf(Long.MAX_VALUE), Value.int64(Long.MAX_VALUE).getAsString()); assertEquals(String.valueOf(Long.MIN_VALUE), Value.int64(Long.MIN_VALUE).getAsString()); + assertEquals("3.14", Value.float32(3.14f).getAsString()); + assertEquals("NaN", Value.float32(Float.NaN).getAsString()); + assertEquals(String.valueOf(Float.MIN_VALUE), Value.float32(Float.MIN_VALUE).getAsString()); + assertEquals(String.valueOf(Float.MAX_VALUE), Value.float32(Float.MAX_VALUE).getAsString()); + assertEquals("3.14", Value.float64(3.14d).getAsString()); assertEquals("NaN", Value.float64(Double.NaN).getAsString()); assertEquals(String.valueOf(Double.MIN_VALUE), Value.float64(Double.MIN_VALUE).getAsString()); @@ -2052,6 +2196,9 @@ public void serialization() { reserializeAndAssert(Value.int64(123)); reserializeAndAssert(Value.int64(null)); + reserializeAndAssert(Value.float32(1.23f)); + reserializeAndAssert(Value.float32(null)); + reserializeAndAssert(Value.float64(1.23)); reserializeAndAssert(Value.float64(null)); @@ -2089,6 +2236,10 @@ public void serialization() { reserializeAndAssert(Value.int64Array(new long[] {1L, 2L})); reserializeAndAssert(Value.int64Array((Iterable) null)); + reserializeAndAssert(Value.float32Array(new float[] {.1f, .2f})); + reserializeAndAssert(Value.float32Array(BrokenSerializationList.of(.1f, .2f, .3f))); + reserializeAndAssert(Value.float32Array((Iterable) null)); + reserializeAndAssert(Value.float64Array(new double[] {.1, .2})); reserializeAndAssert(Value.float64Array(BrokenSerializationList.of(.1, .2, .3))); reserializeAndAssert(Value.float64Array((Iterable) null)); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java index 4e8fb0cfcb..759f058aa0 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ChecksumResultSetTest.java @@ -57,6 +57,8 @@ public class ChecksumResultSetTest { .to(2 * 2) .set("doubleVal") .to(Value.float64(3.14d * 2d)) + .set("floatVal") + .to(Value.float32(3.14f * 3f)) .set("bigDecimalVal") .to(Value.numeric(BigDecimal.valueOf(123 * 2, 2))) .set("pgNumericVal") @@ -83,6 +85,8 @@ public class ChecksumResultSetTest { .to(Value.int64Array(Arrays.asList(2L, null, 1L, 0L))) .set("doubleArray") .to(Value.float64Array(Arrays.asList(3.14d, null, 6.6626d, 10.1d))) + .set("floatArray") + .to(Value.float32Array(Arrays.asList(2.71f, null, 6.6626f, 10.1f))) .set("bigDecimalArray") .to(Value.numericArray(Arrays.asList(BigDecimal.TEN, null, BigDecimal.ONE))) .set("pgNumericArray") @@ -128,6 +132,7 @@ public void testRetry() { Type.StructField.of("boolVal", Type.bool()), Type.StructField.of("longVal", Type.int64()), Type.StructField.of("doubleVal", Type.float64()), + Type.StructField.of("floatVal", Type.float32()), Type.StructField.of("bigDecimalVal", Type.numeric()), Type.StructField.of("pgNumericVal", Type.pgNumeric()), Type.StructField.of("stringVal", Type.string()), @@ -143,6 +148,7 @@ public void testRetry() { Type.StructField.of("boolArray", Type.array(Type.bool())), Type.StructField.of("longArray", Type.array(Type.int64())), Type.StructField.of("doubleArray", Type.array(Type.float64())), + Type.StructField.of("floatArray", Type.array(Type.float32())), Type.StructField.of("bigDecimalArray", Type.array(Type.numeric())), Type.StructField.of("pgNumericArray", Type.array(Type.pgNumeric())), Type.StructField.of("byteArray", Type.array(Type.bytes())), @@ -164,6 +170,8 @@ public void testRetry() { .to(2) .set("doubleVal") .to(Value.float64(3.14d)) + .set("floatVal") + .to(Value.float32(2.71f)) .set("bigDecimalVal") .to(Value.numeric(BigDecimal.valueOf(123, 2))) .set("pgNumericVal") @@ -190,6 +198,8 @@ public void testRetry() { .to(Value.int64Array(Arrays.asList(1L, null, 2L))) .set("doubleArray") .to(Value.float64Array(Arrays.asList(3.14d, null, 6.6626d))) + .set("floatArray") + .to(Value.float32Array(Arrays.asList(2.71f, null, 6.6626f))) .set("bigDecimalArray") .to(Value.numericArray(Arrays.asList(BigDecimal.ONE, null, BigDecimal.TEN))) .set("pgNumericArray") @@ -238,6 +248,8 @@ public void testRetry() { .to((Long) null) .set("doubleVal") .to((Double) null) + .set("floatVal") + .to((Float) null) .set("bigDecimalVal") .to((BigDecimal) null) .set("pgNumericVal") @@ -264,6 +276,8 @@ public void testRetry() { .toInt64Array((Iterable) null) .set("doubleArray") .toFloat64Array((Iterable) null) + .set("floatArray") + .toFloat32Array((Iterable) null) .set("bigDecimalArray") .toNumericArray(null) .set("pgNumericArray") diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncExamplesTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncExamplesTest.java index 5a22c64009..87f26da904 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncExamplesTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncExamplesTest.java @@ -344,9 +344,12 @@ public void readOnlyTransaction() throws Exception { values2 = rs.toListAsync(input -> input.getString("StringValue"), executor); } } + + ApiFuture>> allAsListValues = + ApiFutures.allAsList(Arrays.asList(values1, values2)); ApiFuture> allValues = ApiFutures.transform( - ApiFutures.allAsList(Arrays.asList(values1, values2)), + allAsListValues, input -> Iterables.mergeSorted( input, Comparator.comparing(o -> Integer.valueOf(o.substring(1)))), diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITFloat32Test.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITFloat32Test.java new file mode 100644 index 0000000000..de2640b86c --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITFloat32Test.java @@ -0,0 +1,415 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.it; + +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.IntegrationTestEnv; +import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.ParallelIntegrationTest; +import com.google.cloud.spanner.ResultSet; +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.connection.ConnectionOptions; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@Category(ParallelIntegrationTest.class) +@RunWith(Parameterized.class) +public class ITFloat32Test { + + @ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(); + + @Parameterized.Parameters(name = "Dialect = {0}") + public static List data() { + return Arrays.asList( + new DialectTestParameter(Dialect.GOOGLE_STANDARD_SQL), + new DialectTestParameter(Dialect.POSTGRESQL)); + } + + @Parameterized.Parameter() public DialectTestParameter dialect; + + private static DatabaseClient googleStandardSQLClient; + private static DatabaseClient postgreSQLClient; + + private static final String[] GOOGLE_STANDARD_SQL_SCHEMA = + new String[] { + "CREATE TABLE T (" + + " Key STRING(MAX) NOT NULL," + + " Float32Value FLOAT32," + + " Float32ArrayValue ARRAY," + + ") PRIMARY KEY (Key)" + }; + + private static final String[] POSTGRESQL_SCHEMA = + new String[] { + "CREATE TABLE T (" + + " Key VARCHAR PRIMARY KEY," + + " Float32Value REAL," + + " Float32ArrayValue REAL[]" + + ")" + }; + + private static DatabaseClient client; + + private static boolean isUsingCloudDevel() { + String jobType = System.getenv("JOB_TYPE"); + + // Assumes that the jobType contains the string "cloud-devel" to signal that + // the environment is cloud-devel. + return !isNullOrEmpty(jobType) && jobType.contains("cloud-devel"); + } + + @BeforeClass + public static void setUpDatabase() + throws ExecutionException, InterruptedException, TimeoutException { + assumeTrue("FLOAT32 is currently only supported in cloud-devel", isUsingCloudDevel()); + assumeFalse("Emulator does not support FLOAT32 yet", isUsingEmulator()); + + Database googleStandardSQLDatabase = + env.getTestHelper().createTestDatabase(GOOGLE_STANDARD_SQL_SCHEMA); + + googleStandardSQLClient = env.getTestHelper().getDatabaseClient(googleStandardSQLDatabase); + + Database postgreSQLDatabase = + env.getTestHelper() + .createTestDatabase(Dialect.POSTGRESQL, Arrays.asList(POSTGRESQL_SCHEMA)); + postgreSQLClient = env.getTestHelper().getDatabaseClient(postgreSQLDatabase); + } + + @Before + public void before() { + client = + dialect.dialect == Dialect.GOOGLE_STANDARD_SQL ? googleStandardSQLClient : postgreSQLClient; + } + + @AfterClass + public static void tearDown() throws Exception { + ConnectionOptions.closeSpanner(); + } + + /** Sequence used to generate unique keys. */ + private static int seq; + + private static String uniqueString() { + return String.format("k%04d", seq++); + } + + private String lastKey; + + private Timestamp write(Mutation m) { + return client.write(Collections.singletonList(m)); + } + + private Mutation.WriteBuilder baseInsert() { + return Mutation.newInsertOrUpdateBuilder("T").set("Key").to(lastKey = uniqueString()); + } + + private Struct readRow(String table, String key, String... columns) { + return client + .singleUse(TimestampBound.strong()) + .readRow(table, Key.of(key), Arrays.asList(columns)); + } + + private Struct readLastRow(String... columns) { + return readRow("T", lastKey, columns); + } + + @Test + public void writeFloat32() { + write(baseInsert().set("Float32Value").to(2.0f).build()); + Struct row = readLastRow("Float32Value"); + assertFalse(row.isNull(0)); + assertEquals(2.0f, row.getFloat(0), 0.0f); + } + + @Test + public void writeFloat32NonNumbers() { + + write(baseInsert().set("Float32Value").to(Float.NEGATIVE_INFINITY).build()); + Struct row = readLastRow("Float32Value"); + assertFalse(row.isNull(0)); + assertEquals(Float.NEGATIVE_INFINITY, row.getFloat(0), 0.0f); + + write(baseInsert().set("Float32Value").to(Float.POSITIVE_INFINITY).build()); + row = readLastRow("Float32Value"); + assertFalse(row.isNull(0)); + assertEquals(Float.POSITIVE_INFINITY, row.getFloat(0), 0.0); + + write(baseInsert().set("Float32Value").to(Float.NaN).build()); + row = readLastRow("Float32Value"); + assertFalse(row.isNull(0)); + assertTrue(Float.isNaN(row.getFloat(0))); + } + + @Test + public void writeFloat32Null() { + write(baseInsert().set("Float32Value").to((Float) null).build()); + Struct row = readLastRow("Float32Value"); + assertTrue(row.isNull(0)); + } + + @Test + public void writeFloat32ArrayNull() { + write(baseInsert().set("Float32ArrayValue").toFloat32Array((float[]) null).build()); + Struct row = readLastRow("Float32ArrayValue"); + assertTrue(row.isNull(0)); + } + + @Test + public void writeFloat32ArrayEmpty() { + write(baseInsert().set("Float32ArrayValue").toFloat32Array(new float[] {}).build()); + Struct row = readLastRow("Float32ArrayValue"); + assertFalse(row.isNull(0)); + assertTrue(row.getFloatList(0).isEmpty()); + } + + @Test + public void writeFloat32Array() { + write( + baseInsert() + .set("Float32ArrayValue") + .toFloat32Array(Arrays.asList(null, 1.0f, 2.0f)) + .build()); + Struct row = readLastRow("Float32ArrayValue"); + assertFalse(row.isNull(0)); + assertEquals(row.getFloatList(0), Arrays.asList(null, 1.0f, 2.0f)); + assertThrows(NullPointerException.class, () -> row.getFloatArray(0)); + } + + @Test + public void writeFloat32ArrayNoNulls() { + write(baseInsert().set("Float32ArrayValue").toFloat32Array(Arrays.asList(1.0f, 2.0f)).build()); + Struct row = readLastRow("Float32ArrayValue"); + assertFalse(row.isNull(0)); + assertEquals(2, row.getFloatArray(0).length); + assertEquals(1.0f, row.getFloatArray(0)[0], 0.0f); + assertEquals(2.0f, row.getFloatArray(0)[1], 0.0f); + } + + private String getInsertStatementWithLiterals() { + String statement = "INSERT INTO T (Key, Float32Value, Float32ArrayValue) VALUES "; + + if (dialect.dialect == Dialect.POSTGRESQL) { + statement += + "('dml1', 3.14::float8, array[1.1]::float4[]), " + + "('dml2', '3.14'::float4, array[3.14::float4, 3.14::float8]::float4[]), " + + "('dml3', 'nan'::real, array['inf'::real, (3.14::float8)::float4, 1.2, '-inf']::float4[]), " + + "('dml4', 1.175494e-38::real, array[1.175494e-38, 3.4028234e38, -3.4028234e38]::real[]), " + + "('dml5', null, null)"; + } else { + statement += + "('dml1', 3.14, [CAST(1.1 AS FLOAT32)]), " + + "('dml2', CAST('3.14' AS FLOAT32), array[CAST(3.14 AS FLOAT32), 3.14]), " + + "('dml3', CAST('nan' AS FLOAT32), array[CAST('inf' AS FLOAT32), CAST(CAST(3.14 AS FLOAT64) AS FLOAT32), 1.2, CAST('-inf' AS FLOAT32)]), " + + "('dml4', 1.175494e-38, [CAST(1.175494e-38 AS FLOAT32), 3.4028234e38, -3.4028234e38]), " + + "('dml5', null, null)"; + } + return statement; + } + + @Test + public void float32Literals() { + client + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(Statement.of(getInsertStatementWithLiterals())); + return null; + }); + + verifyContents("dml"); + } + + private String getInsertStatementWithParameters() { + String pgStatement = + "INSERT INTO T (Key, Float32Value, Float32ArrayValue) VALUES " + + "('param1', $1, $2), " + + "('param2', $3, $4), " + + "('param3', $5, $6), " + + "('param4', $7, $8), " + + "('param5', $9, $10)"; + + return (dialect.dialect == Dialect.POSTGRESQL) ? pgStatement : pgStatement.replace("$", "@p"); + } + + @Test + public void float32Parameter() { + client + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate( + Statement.newBuilder(getInsertStatementWithParameters()) + .bind("p1") + .to(Value.float32(3.14f)) + .bind("p2") + .to(Value.float32Array(Arrays.asList(1.1f))) + .bind("p3") + .to(Value.float32(3.14f)) + .bind("p4") + .to(Value.float32Array(new float[] {3.14f, 3.14f})) + .bind("p5") + .to(Value.float32(Float.NaN)) + .bind("p6") + .to( + Value.float32Array( + Arrays.asList( + Float.POSITIVE_INFINITY, 3.14f, 1.2f, Float.NEGATIVE_INFINITY))) + .bind("p7") + .to(Value.float32(Float.MIN_NORMAL)) + .bind("p8") + .to( + Value.float32Array( + Arrays.asList( + Float.MIN_NORMAL, Float.MAX_VALUE, -1 * Float.MAX_VALUE))) + .bind("p9") + .to(Value.float32(null)) + .bind("p10") + .to(Value.float32Array((float[]) null)) + .build()); + return null; + }); + + verifyContents("param"); + } + + private String getInsertStatementForUntypedParameters() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return "INSERT INTO T (key, float32value, float32arrayvalue) VALUES " + + "('untyped1', ($1)::float4, ($2)::float4[])"; + } + return "INSERT INTO T (Key, Float32Value, Float32ArrayValue) VALUES " + + "('untyped1', CAST(@p1 AS FLOAT32), CAST(@p2 AS ARRAY))"; + } + + @Test + public void float32UntypedParameter() { + client + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate( + Statement.newBuilder(getInsertStatementForUntypedParameters()) + .bind("p1") + .to( + Value.untyped( + com.google.protobuf.Value.newBuilder() + .setNumberValue((double) 3.14f) + .build())) + .bind("p2") + .to( + Value.untyped( + com.google.protobuf.Value.newBuilder() + .setListValue( + com.google.protobuf.ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setNumberValue((double) Float.MIN_NORMAL))) + .build())) + .build()); + return null; + }); + + Struct row = readRow("T", "untyped1", "Float32Value", "Float32ArrayValue"); + // Float32Value + assertFalse(row.isNull(0)); + assertEquals(3.14f, row.getFloat(0), 0.00001f); + // Float32ArrayValue + assertFalse(row.isNull(1)); + assertEquals(1, row.getFloatList(1).size()); + assertEquals(Float.MIN_NORMAL, row.getFloatList(1).get(0), 0.00001f); + } + + private void verifyContents(String keyPrefix) { + try (ResultSet resultSet = + client + .singleUse() + .executeQuery( + Statement.of( + "SELECT Key AS key, Float32Value AS float32value, Float32ArrayValue AS float32arrayvalue FROM T WHERE Key LIKE '{keyPrefix}%' ORDER BY key" + .replace("{keyPrefix}", keyPrefix)))) { + + assertTrue(resultSet.next()); + + assertEquals(3.14f, resultSet.getFloat("float32value"), 0.00001f); + assertEquals(Value.float32(3.14f), resultSet.getValue("float32value")); + + assertArrayEquals(new float[] {1.1f}, resultSet.getFloatArray("float32arrayvalue"), 0.00001f); + + assertTrue(resultSet.next()); + + assertEquals(3.14f, resultSet.getFloat("float32value"), 0.00001f); + assertEquals(Arrays.asList(3.14f, 3.14f), resultSet.getFloatList("float32arrayvalue")); + assertEquals( + Value.float32Array(new float[] {3.14f, 3.14f}), resultSet.getValue("float32arrayvalue")); + + assertTrue(resultSet.next()); + assertTrue(Float.isNaN(resultSet.getFloat("float32value"))); + assertTrue(Float.isNaN(resultSet.getValue("float32value").getFloat32())); + assertEquals( + Arrays.asList(Float.POSITIVE_INFINITY, 3.14f, 1.2f, Float.NEGATIVE_INFINITY), + resultSet.getFloatList("float32arrayvalue")); + assertEquals( + Value.float32Array( + Arrays.asList(Float.POSITIVE_INFINITY, 3.14f, 1.2f, Float.NEGATIVE_INFINITY)), + resultSet.getValue("float32arrayvalue")); + + assertTrue(resultSet.next()); + assertEquals(Float.MIN_NORMAL, resultSet.getFloat("float32value"), 0.00001f); + assertEquals(Float.MIN_NORMAL, resultSet.getValue("float32value").getFloat32(), 0.00001f); + assertEquals(3, resultSet.getFloatList("float32arrayvalue").size()); + assertEquals(Float.MIN_NORMAL, resultSet.getFloatList("float32arrayvalue").get(0), 0.00001); + assertEquals(Float.MAX_VALUE, resultSet.getFloatList("float32arrayvalue").get(1), 0.00001f); + assertEquals( + -1 * Float.MAX_VALUE, resultSet.getFloatList("float32arrayvalue").get(2), 0.00001f); + assertEquals(3, resultSet.getValue("float32arrayvalue").getFloat32Array().size()); + + assertTrue(resultSet.next()); + assertTrue(resultSet.isNull("float32value")); + assertTrue(resultSet.isNull("float32arrayvalue")); + + assertFalse(resultSet.next()); + } + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java index a691fbf78b..170ce75e69 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITQueryTest.java @@ -17,6 +17,7 @@ package com.google.cloud.spanner.it; import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; +import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; @@ -254,6 +255,36 @@ public void bindInt64Null() { assertThat(row.isNull(0)).isTrue(); } + // TODO: Remove once FLOAT32 is supported in production. + private static boolean isUsingCloudDevel() { + String jobType = System.getenv("JOB_TYPE"); + + // Assumes that the jobType contains the string "cloud-devel" to signal that + // the environment is cloud-devel. + return !isNullOrEmpty(jobType) && jobType.contains("cloud-devel"); + } + + @Test + public void bindFloat32() { + assumeFalse("Emulator does not support FLOAT32 yet", isUsingEmulator()); + assumeTrue("FLOAT32 is currently only supported in cloud-devel", isUsingCloudDevel()); + + Struct row = + execute(Statement.newBuilder(selectValueQuery).bind("p1").to(2.0f), Type.float32()); + assertThat(row.isNull(0)).isFalse(); + assertThat(row.getFloat(0)).isWithin(0.0f).of(2.0f); + } + + @Test + public void bindFloat32Null() { + assumeFalse("Emulator does not support FLOAT32 yet", isUsingEmulator()); + assumeTrue("FLOAT32 is currently only supported in cloud-devel", isUsingCloudDevel()); + + Struct row = + execute(Statement.newBuilder(selectValueQuery).bind("p1").to((Float) null), Type.float32()); + assertThat(row.isNull(0)).isTrue(); + } + @Test public void bindFloat64() { Struct row = execute(Statement.newBuilder(selectValueQuery).bind("p1").to(2.0), Type.float64()); @@ -497,6 +528,58 @@ public void bindInt64ArrayNull() { assertThat(row.isNull(0)).isTrue(); } + @Test + public void bindFloat32Array() { + assumeFalse("Emulator does not support FLOAT32 yet", isUsingEmulator()); + assumeTrue("FLOAT32 is currently only supported in cloud-devel", isUsingCloudDevel()); + + Struct row = + execute( + Statement.newBuilder(selectValueQuery) + .bind("p1") + .toFloat32Array( + asList( + null, + 1.0f, + 2.0f, + Float.NEGATIVE_INFINITY, + Float.POSITIVE_INFINITY, + Float.NaN)), + Type.array(Type.float32())); + assertThat(row.isNull(0)).isFalse(); + assertThat(row.getFloatList(0)) + .containsExactly( + null, 1.0f, 2.0f, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NaN) + .inOrder(); + } + + @Test + public void bindFloat32ArrayEmpty() { + assumeFalse("Emulator does not support FLOAT32 yet", isUsingEmulator()); + assumeTrue("FLOAT32 is currently only supported in cloud-devel", isUsingCloudDevel()); + + Struct row = + execute( + Statement.newBuilder(selectValueQuery) + .bind("p1") + .toFloat32Array(Collections.emptyList()), + Type.array(Type.float32())); + assertThat(row.isNull(0)).isFalse(); + assertThat(row.getFloatList(0)).containsExactly(); + } + + @Test + public void bindFloat32ArrayNull() { + assumeFalse("Emulator does not support FLOAT32 yet", isUsingEmulator()); + assumeTrue("FLOAT32 is currently only supported in cloud-devel", isUsingCloudDevel()); + + Struct row = + execute( + Statement.newBuilder(selectValueQuery).bind("p1").toFloat32Array((float[]) null), + Type.array(Type.float32())); + assertThat(row.isNull(0)).isTrue(); + } + @Test public void bindFloat64Array() { Struct row = From cf865f8dd3f5906805c1481a7e5393d680ea83e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 29 Feb 2024 11:23:34 +0100 Subject: [PATCH 08/16] chore: loosen restrictions setRetryAbortsInternally (#2924) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: loosen restrictions setRetryAbortsInternally Calling setRetryAbortsInternally(boolean) was unnecessarily strict, as there is no reason why it should be disallowed to change the value while in auto-commit mode and/or read-only mode. The fact that the flag does not have any direct impact on those modes, does not mean that it should be disallowed to set the value. The fact that it was impossible to set the value while in auto-commit mode, also means that it is impossible to disable it using a startup command for a PostgreSQL connection to PGAdapter, as PGAdapter creates connections in auto-commit mode. * 🦉 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/ConnectionImpl.java | 4 -- .../connection/ConnectionImplTest.java | 44 +++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java index e9aed66b6b..8ff367b248 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionImpl.java @@ -678,10 +678,6 @@ public void setStatementTag(String tag) { */ private void checkSetRetryAbortsInternallyAvailable() { ConnectionPreconditions.checkState(!isClosed(), CLOSED_ERROR_MSG); - ConnectionPreconditions.checkState(isInTransaction(), "This connection has no transaction"); - ConnectionPreconditions.checkState( - getTransactionMode() == TransactionMode.READ_WRITE_TRANSACTION, - "RetryAbortsInternally is only available for read-write transactions"); ConnectionPreconditions.checkState( !isTransactionStarted(), "RetryAbortsInternally cannot be set after the transaction has started"); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java index 0b54b35225..015927440d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java @@ -1863,6 +1863,50 @@ public void testCheckResultTypeAllowed() { checkResultTypeAllowed(parser.parse(Statement.of(start)), allowedResultTypes); } + @Test + public void testSetRetryAbortsInternally() { + try (ConnectionImpl connection = + createConnection( + ConnectionOptions.newBuilder() + .setCredentials(NoCredentials.getInstance()) + .setUri(URI) + .build())) { + assertFalse("Read-only should be disabled by default", connection.isReadOnly()); + assertTrue("Autocommit should be enabled by default", connection.isAutocommit()); + assertFalse( + "Retry aborts internally should be disabled by default on test connections", + connection.isRetryAbortsInternally()); + + // It should be possible to change this value also when in auto-commit mode. + connection.setRetryAbortsInternally(true); + assertTrue(connection.isRetryAbortsInternally()); + + // It should be possible to change this value also when in transactional mode, as long as + // there is no active transaction. + connection.setAutocommit(false); + connection.setRetryAbortsInternally(false); + assertFalse(connection.isRetryAbortsInternally()); + + // It should be possible to change the value when in read-only mode. + connection.setReadOnly(true); + connection.setRetryAbortsInternally(true); + assertTrue(connection.isRetryAbortsInternally()); + + // It should not be possible to change the value when there is an active transaction. + connection.setReadOnly(false); + connection.setAutocommit(false); + connection.execute(Statement.of(SELECT)); + assertThrows(SpannerException.class, () -> connection.setRetryAbortsInternally(false)); + // Verify that the value did not change. + assertTrue(connection.isRetryAbortsInternally()); + + // Rolling back the connection should allow us to set the property again. + connection.rollback(); + connection.setRetryAbortsInternally(false); + assertFalse(connection.isRetryAbortsInternally()); + } + } + private void assertThrowResultNotAllowed( AbstractStatementParser parser, String sql, ImmutableSet allowedResultTypes) { SpannerException exception = From 2f6182e6402e279d4b93b1f58cbcfae3124d0034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 29 Feb 2024 19:20:14 +0100 Subject: [PATCH 09/16] chore: fix float32 NaN comparison + deprecate generator (#2933) The FLOAT32 type was only added to the com.google.cloud.spanner.RandomResultSetGenerator, but that generator misses multiple other types. The one in the .connection package should be used instead. This PR therefore deprecates the one that should not be used, and updates all tests that used the old one. It also adds FLOAT32 to the .connection.RandomResultSetGenerator, which is used by a larger number of tests. This discovered a bug in the FLOAT32 NaN comparison, which is also fixed in this PR. Note: The commit is not marked with 'fix:', as the feature has not yet been released. --- .../java/com/google/cloud/spanner/Value.java | 4 +++ .../cloud/spanner/AsyncResultSetImplTest.java | 4 +-- .../cloud/spanner/DatabaseClientImplTest.java | 36 +++++++++++++++++++ .../spanner/InlineBeginTransactionTest.java | 1 + .../spanner/RandomResultSetGenerator.java | 2 ++ .../com/google/cloud/spanner/ValueTest.java | 3 ++ .../connection/AbstractMockServerTest.java | 1 - .../connection/MergedResultSetTest.java | 2 +- .../PartitionedQueryMockServerTest.java | 13 ++++--- .../connection/RandomResultSetGenerator.java | 12 +++++++ 10 files changed, 70 insertions(+), 8 deletions(-) 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 e4db5ff146..49a6c1e7bc 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 @@ -1628,6 +1628,10 @@ void valueToString(StringBuilder b) { @Override boolean valueEquals(Value v) { + // NaN == NaN always returns false, so we need a custom check. + if (Float.isNaN(this.value)) { + return Float.isNaN(((Float32Impl) v).value); + } return ((Float32Impl) v).value == value; } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncResultSetImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncResultSetImplTest.java index 9a6bafa7bf..98497fbf14 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncResultSetImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncResultSetImplTest.java @@ -193,9 +193,9 @@ public void withCallback() throws InterruptedException { }); } finishedLatch.await(); - // There should be between 1 and 4 callbacks, depending on the timing of the threads. + // There should be between 1 and 5 callbacks, depending on the timing of the threads. // Normally, there should be just 1 callback. - assertThat(callbackCounter.get()).isIn(Range.closed(1, 4)); + assertThat(callbackCounter.get()).isIn(Range.closed(1, 5)); assertThat(rowCounter.get()).isEqualTo(3); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java index 8dfbb986eb..7cba80edd8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java @@ -4083,6 +4083,7 @@ public void testGetAllTypesAsString() { int col = 0; assertAsString("true", resultSet, col++); assertAsString("100", resultSet, col++); + assertAsString("-3.14", resultSet, col++); assertAsString("3.14", resultSet, col++); assertAsString("6.626", resultSet, col++); assertAsString("test-string", resultSet, col++); @@ -4100,6 +4101,15 @@ public void testGetAllTypesAsString() { String.format("%d", Long.MAX_VALUE), String.format("%d", Long.MIN_VALUE), "NULL"), resultSet, col++); + assertAsString( + ImmutableList.of( + "NULL", + Float.valueOf(Float.MAX_VALUE).toString(), + Float.valueOf(Float.MIN_VALUE).toString(), + "NaN", + "3.14"), + resultSet, + col++); assertAsString(ImmutableList.of("NULL", "-12345.6789", "3.14"), resultSet, col++); assertAsString(ImmutableList.of("6.626", "NULL", "-8.9123"), resultSet, col++); assertAsString(ImmutableList.of("test-string1", "NULL", "test-string2"), resultSet, col++); @@ -4561,6 +4571,7 @@ private ListValue getRows(Dialect dialect) { ListValue.newBuilder() .addValues(com.google.protobuf.Value.newBuilder().setBoolValue(true).build()) .addValues(com.google.protobuf.Value.newBuilder().setStringValue("100").build()) + .addValues(com.google.protobuf.Value.newBuilder().setNumberValue(-3.14f).build()) .addValues(com.google.protobuf.Value.newBuilder().setNumberValue(3.14d).build()) .addValues(com.google.protobuf.Value.newBuilder().setStringValue("6.626").build()) .addValues(com.google.protobuf.Value.newBuilder().setStringValue("test-string").build()) @@ -4609,6 +4620,31 @@ private ListValue getRows(Dialect dialect) { .setNullValue(NullValue.NULL_VALUE) .build()) .build())) + .addValues( + com.google.protobuf.Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + com.google.protobuf.Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNumberValue(Float.MAX_VALUE) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNumberValue(Float.MIN_VALUE) + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setStringValue("NaN") + .build()) + .addValues( + com.google.protobuf.Value.newBuilder() + .setNumberValue(3.14f) + .build()) + .build())) .addValues( com.google.protobuf.Value.newBuilder() .setListValue( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java index c7f5e39606..c67c008467 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/InlineBeginTransactionTest.java @@ -37,6 +37,7 @@ import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; import com.google.cloud.spanner.TransactionRunner.TransactionCallable; import com.google.cloud.spanner.TransactionRunnerImpl.TransactionContextImpl; +import com.google.cloud.spanner.connection.RandomResultSetGenerator; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.AbstractMessage; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RandomResultSetGenerator.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RandomResultSetGenerator.java index 6cf1d0900a..058429d3ba 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RandomResultSetGenerator.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RandomResultSetGenerator.java @@ -31,6 +31,8 @@ import com.google.spanner.v1.TypeCode; import java.util.Random; +/** @deprecated Use {@link com.google.cloud.spanner.connection.RandomResultSetGenerator} instead. */ +@Deprecated public class RandomResultSetGenerator { private static final Type[] TYPES = new Type[] { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java index d692fc44e7..460c646807 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/ValueTest.java @@ -193,6 +193,7 @@ public void float32() { assertThat(v.getFloat32()).isWithin(0.0001f).of(1.23f); assertThat(v.toString()).isEqualTo("1.23"); assertEquals("1.23", v.getAsString()); + assertEquals(Value.float32(Float.NaN), Value.float32(Float.NaN)); } @Test @@ -224,6 +225,7 @@ public void float64() { assertThat(v.getFloat64()).isWithin(0.0001).of(1.23); assertThat(v.toString()).isEqualTo("1.23"); assertEquals("1.23", v.getAsString()); + assertEquals(Value.float64(Double.NaN), Value.float64(Double.NaN)); } @Test @@ -267,6 +269,7 @@ public void pgNumeric() { assertEquals(1234.5678D, value.getFloat64(), 0.00001); assertEquals("1234.5678", value.toString()); assertEquals("1234.5678", value.getAsString()); + assertEquals(Value.pgNumeric("NaN"), Value.pgNumeric("NaN")); } @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java index af67859b4a..9af443293a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AbstractMockServerTest.java @@ -19,7 +19,6 @@ import com.google.cloud.spanner.ForceCloseSpannerFunction; import com.google.cloud.spanner.MockSpannerServiceImpl; import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; -import com.google.cloud.spanner.RandomResultSetGenerator; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.admin.database.v1.MockDatabaseAdminImpl; import com.google.cloud.spanner.admin.instance.v1.MockInstanceAdminImpl; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/MergedResultSetTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/MergedResultSetTest.java index 3cf717e176..8a309115c7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/MergedResultSetTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/MergedResultSetTest.java @@ -150,7 +150,7 @@ public void testAllResultsAreReturned() { if (numPartitions == 0) { assertEquals(0, resultSet.getColumnCount()); } else { - assertEquals(22, resultSet.getColumnCount()); + assertEquals(24, resultSet.getColumnCount()); assertEquals(Type.bool(), resultSet.getColumnType(0)); assertEquals(Type.bool(), resultSet.getColumnType("COL0")); assertEquals(10, resultSet.getColumnIndex("COL10")); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/PartitionedQueryMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/PartitionedQueryMockServerTest.java index e5731ff0f6..2536b74f10 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/PartitionedQueryMockServerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/PartitionedQueryMockServerTest.java @@ -58,15 +58,20 @@ public static Object[] data() { @Parameter public Dialect dialect; + private Dialect currentDialect; + @Before public void setupDialect() { - mockSpanner.putStatementResult(StatementResult.detectDialectResult(dialect)); + if (currentDialect != dialect) { + mockSpanner.putStatementResult(StatementResult.detectDialectResult(dialect)); + SpannerPool.closeSpannerPool(); + currentDialect = dialect; + } } @After public void clearRequests() { mockSpanner.clearRequests(); - SpannerPool.closeSpannerPool(); } @Test @@ -349,9 +354,9 @@ public void testRunEmptyPartitionedQuery() { statement, PartitionOptions.newBuilder().setMaxPartitions(maxPartitions).build())) { assertFalse(resultSet.next()); assertNotNull(resultSet.getMetadata()); - assertEquals(22, resultSet.getMetadata().getRowType().getFieldsCount()); + assertEquals(24, resultSet.getMetadata().getRowType().getFieldsCount()); assertNotNull(resultSet.getType()); - assertEquals(22, resultSet.getType().getStructFields().size()); + assertEquals(24, resultSet.getType().getStructFields().size()); } } assertEquals(1, mockSpanner.countRequestsOfType(CreateSessionRequest.class)); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java index 2067d36b5e..ef38df0db3 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/RandomResultSetGenerator.java @@ -51,6 +51,7 @@ public static Type[] generateAllTypes(Dialect dialect) { Arrays.asList( Type.newBuilder().setCode(TypeCode.BOOL).build(), Type.newBuilder().setCode(TypeCode.INT64).build(), + Type.newBuilder().setCode(TypeCode.FLOAT32).build(), Type.newBuilder().setCode(TypeCode.FLOAT64).build(), dialect == Dialect.POSTGRESQL ? Type.newBuilder() @@ -76,6 +77,10 @@ public static Type[] generateAllTypes(Dialect dialect) { .setCode(TypeCode.ARRAY) .setArrayElementType(Type.newBuilder().setCode(TypeCode.INT64)) .build(), + Type.newBuilder() + .setCode(TypeCode.ARRAY) + .setArrayElementType(Type.newBuilder().setCode(TypeCode.FLOAT32)) + .build(), Type.newBuilder() .setCode(TypeCode.ARRAY) .setArrayElementType(Type.newBuilder().setCode(TypeCode.FLOAT64)) @@ -229,6 +234,13 @@ private void setRandomValue(Value.Builder builder, Type type) { random.nextInt(2019) + 1, random.nextInt(11) + 1, random.nextInt(28) + 1); builder.setStringValue(date.toString()); break; + case FLOAT32: + if (randomNaN()) { + builder.setNumberValue(Float.NaN); + } else { + builder.setNumberValue(random.nextFloat()); + } + break; case FLOAT64: if (randomNaN()) { builder.setNumberValue(Double.NaN); From 9a8cce8aa33903aba3c281d5d8542a581023567a Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Sat, 2 Mar 2024 15:36:18 +0530 Subject: [PATCH 10/16] test: add a default benchmark for measuring baselines. (#2739) * Adding a benchmark (along with usual considerations) for measuring the latencies of some Java Client APIs. For any new feature, we usually first define the baseline by running benchmarks (without the feature). * Adding few utility methods to measure the p99/p95/p50 latencies. * Added benchmarking best-practices to avoid hotspot (by randomising keys), avoid cold start, etc. --- .../spanner/AbstractLatencyBenchmark.java | 62 ++++ .../spanner/BenchmarkingUtilityScripts.java | 142 +++++++++ .../cloud/spanner/DefaultBenchmark.java | 270 ++++++++++++++++++ 3 files changed, 474 insertions(+) create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractLatencyBenchmark.java create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/BenchmarkingUtilityScripts.java create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/DefaultBenchmark.java diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractLatencyBenchmark.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractLatencyBenchmark.java new file mode 100644 index 0000000000..d7edf64030 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AbstractLatencyBenchmark.java @@ -0,0 +1,62 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public abstract class AbstractLatencyBenchmark { + + /** Utility to print latency numbers. It computes metrics such as Average, P50, P95 and P99. */ + public void printResults(List results) { + if (results == null) { + return; + } + List orderedResults = new ArrayList<>(results); + Collections.sort(orderedResults); + System.out.println(); + System.out.printf("Total number of queries: %d\n", orderedResults.size()); + System.out.printf("Avg: %fs\n", avg(results)); + System.out.printf("P50: %fs\n", percentile(50, orderedResults)); + System.out.printf("P95: %fs\n", percentile(95, orderedResults)); + System.out.printf("P99: %fs\n", percentile(99, orderedResults)); + } + + private double percentile(int percentile, List orderedResults) { + int index = percentile * orderedResults.size() / 100; + Duration value = orderedResults.get(index); + Double convertedValue = convertDurationToFractionInSeconds(value); + return convertedValue; + } + + /** Returns the average duration in seconds from a list of duration values. */ + private double avg(List results) { + return results.stream() + .collect(Collectors.averagingDouble(this::convertDurationToFractionInSeconds)); + } + + private double convertDurationToFractionInSeconds(Duration duration) { + long seconds = duration.getSeconds(); + long nanos = duration.getNano(); + double fraction = (double) nanos / 1_000_000_000; + double value = seconds + fraction; + return value; + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BenchmarkingUtilityScripts.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BenchmarkingUtilityScripts.java new file mode 100644 index 0000000000..75e0ca3da0 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/BenchmarkingUtilityScripts.java @@ -0,0 +1,142 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import com.google.api.gax.rpc.ServerStream; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningScheduledExecutorService; +import com.google.rpc.Code; +import com.google.spanner.v1.BatchWriteResponse; +import java.time.Duration; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Hosts a bunch of utility methods/scripts that can be used while performing benchmarks to load + * data, report latency metrics, etc. + * + *

Table schema used here: CREATE TABLE FOO ( id INT64 NOT NULL, BAZ INT64, BAR INT64, ) PRIMARY + * KEY(id); + */ +@Category(SlowTest.class) +@RunWith(JUnit4.class) +public class BenchmarkingUtilityScripts { + + // TODO(developer): Add your values here for PROJECT_ID, INSTANCE_ID, DATABASE_ID + // TODO(developer): By default these values are blank and should be replaced before a run. + private static final String PROJECT_ID = ""; + private static final String INSTANCE_ID = ""; + private static final String DATABASE_ID = ""; + private static final String SERVER_URL = "https://staging-wrenchworks.sandbox.googleapis.com"; + private static DatabaseClient client; + private static Spanner spanner; + + @BeforeClass + public static void beforeClass() { + final SpannerOptions.Builder optionsBuilder = + SpannerOptions.newBuilder() + .setProjectId(PROJECT_ID) + .setAutoThrottleAdministrativeRequests(); + if (!SERVER_URL.isEmpty()) { + optionsBuilder.setHost(SERVER_URL); + } + final SpannerOptions options = optionsBuilder.build(); + spanner = options.getService(); + client = spanner.getDatabaseClient(DatabaseId.of(PROJECT_ID, INSTANCE_ID, DATABASE_ID)); + + // Delete all existing data from the table + client.write(ImmutableList.of(Mutation.delete("FOO", KeySet.all()))); + } + + @AfterClass + public static void afterClass() { + spanner.close(); + } + + /** + * A utility which bulk inserts 10^6 records into the database in batches. The method assumes that + * the instance/database/table is already created. It does not perform any admin operations. + * + *

Table schema used here: CREATE TABLE FOO ( id INT64 NOT NULL, BAZ INT64, BAR INT64, ) + * PRIMARY KEY(id); + */ + @Test + public void bulkInsertTestData() { + int key = 0; + List mutationGroups = new ArrayList<>(); + for (int batch = 0; batch < 100; batch++) { + List mutations = new LinkedList<>(); + for (int i = 0; i < 10000; i++) { + mutations.add( + Mutation.newInsertBuilder("FOO") + .set("id") + .to(key) + .set("BAZ") + .to(1) + .set("BAR") + .to(2) + .build()); + key++; + } + mutationGroups.add(MutationGroup.of(mutations)); + } + ServerStream responses = client.batchWriteAtLeastOnce(mutationGroups); + for (BatchWriteResponse response : responses) { + if (response.getStatus().getCode() == Code.OK_VALUE) { + System.out.printf( + "Mutation group indexes %s have been applied with commit timestamp %s", + response.getIndexesList(), response.getCommitTimestamp()); + } else { + System.out.printf( + "Mutation group indexes %s could not be applied with error code %s and " + + "error message %s", + response.getIndexesList(), + Code.forNumber(response.getStatus().getCode()), + response.getStatus().getMessage()); + } + } + } + + /** Collects all results from a collection of future objects. */ + public static List collectResults( + final ListeningScheduledExecutorService service, + final List>> results, + final int numOperations, + final Duration timeoutDuration) + throws Exception { + service.shutdown(); + if (!service.awaitTermination(timeoutDuration.toMinutes(), TimeUnit.MINUTES)) { + throw new TimeoutException(); + } + List allResults = new ArrayList<>(numOperations); + for (Future> result : results) { + allResults.addAll(result.get()); + } + return allResults; + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DefaultBenchmark.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DefaultBenchmark.java new file mode 100644 index 0000000000..84578dd323 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DefaultBenchmark.java @@ -0,0 +1,270 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner; + +import static com.google.cloud.spanner.BenchmarkingUtilityScripts.collectResults; +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.base.Stopwatch; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningScheduledExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.AuxCounters; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +/** + * Benchmarks for measuring existing latencies of various APIs using the Java Client. The benchmarks + * are bound to the Maven profile `benchmark` and can be executed like this: mvn clean test + * -DskipTests -Pbenchmark -Dbenchmark.name=DefaultBenchmark + * Test Table Schema : + * + *

CREATE TABLE FOO ( id INT64 NOT NULL, BAZ INT64, BAR INT64, ) PRIMARY KEY(id); + * + *

Below are a few considerations here: 1. We use all default options for this test because that + * is what most customers would be using. 2. The test schema uses a numeric primary key. To ensure + * that the reads/updates are distributed across a large query space, we insert 10^5 records. + * Utility at {@link BenchmarkingUtilityScripts} can be used for loading data. 3. For queries, we + * make sure that the query is sampled randomly across a large query space. This ensure we don't + * cause hot-spots. 4. For avoid cold start issues, we execute 1 query/update and ignore its latency + * from the final reported metrics. + */ +@BenchmarkMode(Mode.AverageTime) +@Fork(value = 1, warmups = 0) +@Measurement(batchSize = 1, iterations = 1, timeUnit = TimeUnit.MILLISECONDS) +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 1) +public class DefaultBenchmark extends AbstractLatencyBenchmark { + + private static final String SELECT_QUERY = "SELECT ID FROM FOO WHERE ID = @id"; + private static final String UPDATE_QUERY = "UPDATE FOO SET BAR=1 WHERE ID = @id"; + private static final String ID_COLUMN_NAME = "id"; + + /** + * Used to determine how many concurrent requests are allowed. For ex - To simulate a low QPS + * scenario, using 1 thread means there will be 1 request. Use a value > 1 to have concurrent + * requests. + */ + private static final int PARALLEL_THREADS = 1; + + /** + * Total number of reads per test run for 1 thread. Increasing the value here will increase the + * duration of the benchmark. For ex - With PARALLEL_THREADS = 2, TOTAL_READS_PER_RUN = 200, there + * will be 400 read requests (200 on each thread). + */ + private static final int TOTAL_READS_PER_RUN = 12000; + + /** + * Total number of writes per test run for 1 thread. Increasing the value here will increase the + * duration of the benchmark. For ex - With PARALLEL_THREADS = 2, TOTAL_WRITES_PER_RUN = 200, + * there will be 400 write requests (200 on each thread). + */ + private static final int TOTAL_WRITES_PER_RUN = 4000; + + /** + * Number of requests which are used to initialise/warmup the benchmark. The latency number of + * these runs are ignored from the final reported results. + */ + private static final int WARMUP_REQUEST_COUNT = 1; + + /** + * Numbers of records in the sample table used in the benchmark. This is used in this benchmark to + * randomly choose a primary key and ensure that the reads are randomly distributed. This is done + * to ensure we don't end up reading/writing the same table record (leading to hot-spotting). + */ + private static final int TOTAL_RECORDS = 1000000; + + @State(Scope.Thread) + @AuxCounters(org.openjdk.jmh.annotations.AuxCounters.Type.EVENTS) + public static class BenchmarkState { + + // TODO(developer): Add your values here for PROJECT_ID, INSTANCE_ID, DATABASE_ID + private static final String INSTANCE_ID = ""; + private static final String DATABASE_ID = ""; + private static final String SERVER_URL = "https://staging-wrenchworks.sandbox.googleapis.com"; + private Spanner spanner; + private DatabaseClientImpl client; + + @Setup(Level.Iteration) + public void setup() throws Exception { + SpannerOptions options = + SpannerOptions.newBuilder() + .setSessionPoolOption( + SessionPoolOptions.newBuilder() + .setWaitForMinSessions(org.threeten.bp.Duration.ofSeconds(20)) + .build()) + .setHost(SERVER_URL) + .build(); + spanner = options.getService(); + client = + (DatabaseClientImpl) + spanner.getDatabaseClient( + DatabaseId.of(options.getProjectId(), INSTANCE_ID, DATABASE_ID)); + } + + @TearDown(Level.Iteration) + public void teardown() throws Exception { + spanner.close(); + } + } + + /** Measures the time needed to execute a burst of queries. */ + @Benchmark + public void burstQueries(final BenchmarkState server) throws Exception { + final DatabaseClientImpl client = server.client; + SessionPool pool = client.pool; + assertThat(pool.totalSessions()) + .isEqualTo(server.spanner.getOptions().getSessionPoolOptions().getMinSessions()); + + ListeningScheduledExecutorService service = + MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(PARALLEL_THREADS)); + List>> results = new ArrayList<>(PARALLEL_THREADS); + for (int i = 0; i < PARALLEL_THREADS; i++) { + results.add(service.submit(() -> runBenchmarksForQueries(server, TOTAL_READS_PER_RUN))); + } + collectResultsAndPrint(service, results, TOTAL_READS_PER_RUN); + } + + /** Measures the time needed to execute a burst of read and write requests. */ + @Benchmark + public void burstQueriesAndWrites(final BenchmarkState server) throws Exception { + final DatabaseClientImpl client = server.client; + SessionPool pool = client.pool; + assertThat(pool.totalSessions()) + .isEqualTo(server.spanner.getOptions().getSessionPoolOptions().getMinSessions()); + + ListeningScheduledExecutorService service = + MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(PARALLEL_THREADS)); + List>> results = new ArrayList<>(PARALLEL_THREADS); + for (int i = 0; i < PARALLEL_THREADS; i++) { + results.add(service.submit(() -> runBenchmarksForQueries(server, TOTAL_READS_PER_RUN))); + } + for (int i = 0; i < PARALLEL_THREADS; i++) { + results.add(service.submit(() -> runBenchmarkForUpdates(server, TOTAL_WRITES_PER_RUN))); + } + + collectResultsAndPrint(service, results, TOTAL_READS_PER_RUN + TOTAL_WRITES_PER_RUN); + } + + /** Measures the time needed to execute a burst of read and write requests. */ + @Benchmark + public void burstUpdates(final BenchmarkState server) throws Exception { + final DatabaseClientImpl client = server.client; + SessionPool pool = client.pool; + assertThat(pool.totalSessions()) + .isEqualTo(server.spanner.getOptions().getSessionPoolOptions().getMinSessions()); + + ListeningScheduledExecutorService service = + MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(PARALLEL_THREADS)); + List>> results = new ArrayList<>(PARALLEL_THREADS); + for (int i = 0; i < PARALLEL_THREADS; i++) { + results.add(service.submit(() -> runBenchmarkForUpdates(server, TOTAL_WRITES_PER_RUN))); + } + + collectResultsAndPrint(service, results, TOTAL_WRITES_PER_RUN); + } + + private List runBenchmarksForQueries( + final BenchmarkState server, int numberOfOperations) { + List results = new ArrayList<>(numberOfOperations); + // Execute one query to make sure everything has been warmed up. + executeWarmup(server); + + for (int i = 0; i < numberOfOperations; i++) { + results.add(executeQuery(server)); + } + return results; + } + + private void executeWarmup(final BenchmarkState server) { + for (int i = 0; i < WARMUP_REQUEST_COUNT; i++) { + executeQuery(server); + } + } + + private java.time.Duration executeQuery(final BenchmarkState server) { + Stopwatch watch = Stopwatch.createStarted(); + + try (ResultSet rs = server.client.singleUse().executeQuery(getRandomisedReadStatement())) { + while (rs.next()) { + int count = rs.getColumnCount(); + } + } + return watch.elapsed(); + } + + private List runBenchmarkForUpdates( + final BenchmarkState server, int numberOfOperations) { + List results = new ArrayList<>(numberOfOperations); + // Execute one query to make sure everything has been warmed up. + executeWarmup(server); + + // Execute one update to make sure everything has been warmed up. + executeUpdate(server); + + for (int i = 0; i < numberOfOperations; i++) { + results.add(executeUpdate(server)); + } + return results; + } + + private Duration executeUpdate(final BenchmarkState server) { + Stopwatch watch = Stopwatch.createStarted(); + + TransactionRunner runner = server.client.readWriteTransaction(); + runner.run(transaction -> transaction.executeUpdate(getRandomisedUpdateStatement())); + + return watch.elapsed(); + } + + static Statement getRandomisedReadStatement() { + int randomKey = ThreadLocalRandom.current().nextInt(TOTAL_RECORDS); + return Statement.newBuilder(SELECT_QUERY).bind(ID_COLUMN_NAME).to(randomKey).build(); + } + + static Statement getRandomisedUpdateStatement() { + int randomKey = ThreadLocalRandom.current().nextInt(TOTAL_RECORDS); + return Statement.newBuilder(UPDATE_QUERY).bind(ID_COLUMN_NAME).to(randomKey).build(); + } + + void collectResultsAndPrint( + ListeningScheduledExecutorService service, + List>> results, + int numOperationsPerThread) + throws Exception { + final List collectResults = + collectResults( + service, results, numOperationsPerThread * PARALLEL_THREADS, Duration.ofMinutes(60)); + printResults(collectResults); + } +} From ccb110ad6835557870933c95cfd76580fd317a16 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 4 Mar 2024 12:24:07 +0530 Subject: [PATCH 11/16] docs: update all public documents to use auto-generated admin clients. (#2928) 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. * docs: update all public documents to use auto-generated admin clients. * chore: fix lint issue. * 🦉 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 | 75 +- samples/README.md | 2 +- samples/snippets/pom.xml | 2 +- .../spanner/AddAndDropDatabaseRole.java | 82 +-- .../example/spanner/AddJsonColumnSample.java | 37 +- .../example/spanner/AddJsonbColumnSample.java | 38 +- .../spanner/AddNumericColumnSample.java | 38 +- .../example/spanner/AlterSequenceSample.java | 18 +- ...ableWithForeignKeyDeleteCascadeSample.java | 44 +- .../com/example/spanner/CopyBackupSample.java | 82 +-- .../CreateBackupWithEncryptionKey.java | 65 +- ...CreateDatabaseWithDefaultLeaderSample.java | 85 +-- .../CreateDatabaseWithEncryptionKey.java | 60 +- ...abaseWithVersionRetentionPeriodSample.java | 56 +- .../spanner/CreateInstanceConfigSample.java | 76 +- .../spanner/CreateInstanceExample.java | 56 +- ...eInstanceWithAutoscalingConfigExample.java | 97 +-- ...ateInstanceWithProcessingUnitsExample.java | 78 +- .../example/spanner/CreateSequenceSample.java | 21 +- ...ableWithForeignKeyDeleteCascadeSample.java | 56 +- .../spanner/DeleteInstanceConfigSample.java | 28 +- ...reignKeyConstraintDeleteCascadeSample.java | 40 +- .../example/spanner/DropSequenceSample.java | 20 +- .../spanner/EnableFineGrainedAccess.java | 50 +- .../example/spanner/GetDatabaseDdlSample.java | 19 +- .../spanner/GetInstanceConfigSample.java | 35 +- .../example/spanner/ListDatabaseRoles.java | 29 +- .../example/spanner/ListDatabasesSample.java | 27 +- .../ListInstanceConfigOperationsSample.java | 61 +- .../spanner/ListInstanceConfigsSample.java | 30 +- .../spanner/PgAlterSequenceSample.java | 20 +- .../spanner/PgCaseSensitivitySample.java | 42 +- .../spanner/PgCreateSequenceSample.java | 21 +- .../example/spanner/PgDropSequenceSample.java | 20 +- .../spanner/PgInterleavedTableSample.java | 45 +- .../com/example/spanner/PgSpannerSample.java | 490 +++++++------ .../RestoreBackupWithEncryptionKey.java | 45 +- .../com/example/spanner/SpannerSample.java | 660 +++++++++-------- .../example/spanner/UpdateDatabaseSample.java | 40 +- ...UpdateDatabaseWithDefaultLeaderSample.java | 27 +- .../spanner/UpdateInstanceConfigSample.java | 59 +- .../example/spanner/admin/archived/.gitkeep | 0 .../archived/AddAndDropDatabaseRole.java | 88 +++ .../AddJsonColumnSample.java | 39 +- .../AddJsonbColumnSample.java | 40 +- .../AddNumericColumnSample.java | 40 +- .../AlterSequenceSample.java | 20 +- ...ableWithForeignKeyDeleteCascadeSample.java | 46 +- .../admin/archived/CopyBackupSample.java | 101 +++ .../CreateBackupWithEncryptionKey.java | 67 +- ...CreateDatabaseWithDefaultLeaderSample.java | 85 +++ .../CreateDatabaseWithEncryptionKey.java | 62 +- ...abaseWithVersionRetentionPeriodSample.java | 58 +- .../archived/CreateInstanceConfigSample.java | 74 ++ .../CreateInstanceExample.java | 58 +- ...eInstanceWithAutoscalingConfigExample.java | 84 +++ ...ateInstanceWithProcessingUnitsExample.java | 76 ++ .../CreateSequenceSample.java | 23 +- ...ableWithForeignKeyDeleteCascadeSample.java | 66 ++ .../DeleteInstanceConfigSample.java | 30 +- ...reignKeyConstraintDeleteCascadeSample.java | 42 +- .../DropSequenceSample.java | 22 +- .../EnableFineGrainedAccess.java | 52 +- .../GetDatabaseDdlSample.java | 21 +- .../GetInstanceConfigSample.java | 37 +- .../ListDatabaseRoles.java | 31 +- .../ListDatabasesSample.java | 29 +- .../ListInstanceConfigOperationsSample.java | 66 ++ .../ListInstanceConfigsSample.java | 32 +- .../PgAlterSequenceSample.java | 22 +- .../PgCaseSensitivitySample.java | 44 +- .../PgCreateSequenceSample.java | 23 +- .../PgDropSequenceSample.java | 22 +- .../PgInterleavedTableSample.java | 47 +- .../PgSpannerSample.java | 492 ++++++------- .../RestoreBackupWithEncryptionKey.java | 47 +- .../SpannerSample.java | 662 ++++++++--------- .../UpdateDatabaseSample.java | 42 +- ...UpdateDatabaseWithDefaultLeaderSample.java | 29 +- .../UpdateInstanceConfigSample.java | 61 +- .../generated/AddAndDropDatabaseRole.java | 80 -- .../admin/generated/CopyBackupSample.java | 97 --- .../generated/CreateInstanceConfigSample.java | 94 --- ...eInstanceWithAutoscalingConfigExample.java | 93 --- ...ateInstanceWithProcessingUnitsExample.java | 80 -- ...ableWithForeignKeyDeleteCascadeSample.java | 66 -- .../ListInstanceConfigOperationsSample.java | 73 -- ...leWithForeignKeyDeleteCascadeSampleIT.java | 20 +- .../{admin/generated => }/CopyBackupIT.java | 3 +- ...eateDatabaseWithDefaultLeaderSampleIT.java | 29 +- ...aseWithVersionRetentionPeriodSampleIT.java | 7 +- ...InstanceWithAutoscalingConfigSampleIT.java | 20 +- ...teInstanceWithProcessingUnitsSampleIT.java | 3 +- ...leWithForeignKeyDeleteCascadeSampleIT.java | 16 +- .../spanner/CustomInstanceConfigSampleIT.java | 36 +- .../com/example/spanner/DatabaseRolesIT.java | 29 +- ...ignKeyConstraintDeleteCascadeSampleIT.java | 22 +- .../com/example/spanner/EncryptionKeyIT.java | 24 +- .../spanner/GetDatabaseDdlSampleIT.java | 48 +- .../generated => }/ListDatabasesIT.java | 3 +- .../spanner/PgCaseSensitivitySampleIT.java | 22 +- .../spanner/PgInterleavedTableSampleIT.java | 22 +- .../example/spanner/PgSpannerSampleIT.java | 61 +- .../PgSpannerStandaloneExamplesIT.java | 7 +- .../generated => }/SampleTestBaseV2.java | 2 +- .../com/example/spanner/SequenceSampleIT.java | 52 +- .../com/example/spanner/SpannerSampleIT.java | 489 +++++++----- .../spanner/SpannerStandaloneExamplesIT.java | 15 +- .../spanner/UpdateDatabaseSampleIT.java | 37 +- ...dateDatabaseWithDefaultLeaderSampleIT.java | 35 +- ...leWithForeignKeyDeleteCascadeSampleIT.java | 23 +- ...eateDatabaseWithDefaultLeaderSampleIT.java | 61 ++ ...aseWithVersionRetentionPeriodSampleIT.java | 9 +- ...InstanceWithAutoscalingConfigSampleIT.java | 23 +- ...leWithForeignKeyDeleteCascadeSampleIT.java | 17 +- .../CustomInstanceConfigSampleIT.java | 39 +- .../DatabaseRolesIT.java | 32 +- ...ignKeyConstraintDeleteCascadeSampleIT.java | 23 +- .../EncryptionKeyIT.java | 26 +- .../GetDatabaseDdlSampleIT.java | 51 +- .../archived}/GetInstanceConfigSampleIT.java | 4 +- .../ListInstanceConfigsSampleIT.java | 4 +- .../PgCaseSensitivitySampleIT.java | 25 +- .../PgInterleavedTableSampleIT.java | 25 +- .../PgSpannerSampleIT.java | 63 +- .../SequenceSampleIT.java | 55 +- .../admin/archived/SpannerSampleIT.java | 593 +++++++++++++++ .../UpdateDatabaseSampleIT.java | 41 +- ...dateDatabaseWithDefaultLeaderSampleIT.java | 39 +- .../admin/generated/SpannerSampleIT.java | 694 ------------------ 130 files changed, 4674 insertions(+), 4514 deletions(-) delete mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/archived/.gitkeep create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/archived/AddAndDropDatabaseRole.java rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/AddJsonColumnSample.java (55%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/AddJsonbColumnSample.java (56%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/AddNumericColumnSample.java (55%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/AlterSequenceSample.java (89%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/AlterTableWithForeignKeyDeleteCascadeSample.java (54%) create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/archived/CopyBackupSample.java rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/CreateBackupWithEncryptionKey.java (58%) create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSample.java rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/CreateDatabaseWithEncryptionKey.java (60%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/CreateDatabaseWithVersionRetentionPeriodSample.java (55%) create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceConfigSample.java rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/CreateInstanceExample.java (56%) create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigExample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithProcessingUnitsExample.java rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/CreateSequenceSample.java (89%) create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateTableWithForeignKeyDeleteCascadeSample.java rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/DeleteInstanceConfigSample.java (60%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/DropForeignKeyConstraintDeleteCascadeSample.java (58%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/DropSequenceSample.java (84%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/EnableFineGrainedAccess.java (63%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/GetDatabaseDdlSample.java (67%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/GetInstanceConfigSample.java (54%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/ListDatabaseRoles.java (54%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/ListDatabasesSample.java (61%) create mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/archived/ListInstanceConfigOperationsSample.java rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/ListInstanceConfigsSample.java (57%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/PgAlterSequenceSample.java (89%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/PgCaseSensitivitySample.java (79%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/PgCreateSequenceSample.java (88%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/PgDropSequenceSample.java (84%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/PgInterleavedTableSample.java (62%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/PgSpannerSample.java (80%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/RestoreBackupWithEncryptionKey.java (65%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/SpannerSample.java (79%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/UpdateDatabaseSample.java (62%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/UpdateDatabaseWithDefaultLeaderSample.java (76%) rename samples/snippets/src/main/java/com/example/spanner/admin/{generated => archived}/UpdateInstanceConfigSample.java (52%) delete mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/AddAndDropDatabaseRole.java delete mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java delete mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceConfigSample.java delete mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigExample.java delete mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsExample.java delete mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateTableWithForeignKeyDeleteCascadeSample.java delete mode 100644 samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigOperationsSample.java rename samples/snippets/src/test/java/com/example/spanner/{admin/generated => }/CopyBackupIT.java (98%) rename samples/snippets/src/test/java/com/example/spanner/{admin/generated => }/CreateInstanceWithProcessingUnitsSampleIT.java (93%) rename samples/snippets/src/test/java/com/example/spanner/{admin/generated => }/ListDatabasesIT.java (92%) rename samples/snippets/src/test/java/com/example/spanner/{admin/generated => }/SampleTestBaseV2.java (99%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/AlterTableWithForeignKeyDeleteCascadeSampleIT.java (77%) create mode 100644 samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSampleIT.java rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/CreateDatabaseWithVersionRetentionPeriodSampleIT.java (84%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/CreateInstanceWithAutoscalingConfigSampleIT.java (58%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/CreateTableWithForeignKeyDeleteCascadeSampleIT.java (71%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/CustomInstanceConfigSampleIT.java (64%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/DatabaseRolesIT.java (82%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/DropForeignKeyConstraintDeleteCascadeSampleIT.java (77%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/EncryptionKeyIT.java (85%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/GetDatabaseDdlSampleIT.java (56%) rename samples/snippets/src/test/java/com/example/spanner/{ => admin/archived}/GetInstanceConfigSampleIT.java (90%) rename samples/snippets/src/test/java/com/example/spanner/{ => admin/archived}/ListInstanceConfigsSampleIT.java (89%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/PgCaseSensitivitySampleIT.java (62%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/PgInterleavedTableSampleIT.java (61%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/PgSpannerSampleIT.java (84%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/SequenceSampleIT.java (69%) create mode 100644 samples/snippets/src/test/java/com/example/spanner/admin/archived/SpannerSampleIT.java rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/UpdateDatabaseSampleIT.java (53%) rename samples/snippets/src/test/java/com/example/spanner/admin/{generated => archived}/UpdateDatabaseWithDefaultLeaderSampleIT.java (61%) delete mode 100644 samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java diff --git a/README.md b/README.md index 2ed6ff04b4..2172a1582f 100644 --- a/README.md +++ b/README.md @@ -509,43 +509,44 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | Update Jsonb Data Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/UpdateJsonbDataSample.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/UpdateJsonbDataSample.java) | | Update Numeric Data Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/UpdateNumericDataSample.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/UpdateNumericDataSample.java) | | Update Using Dml Returning Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/UpdateUsingDmlReturningSample.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/UpdateUsingDmlReturningSample.java) | -| Add And Drop Database Role | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddAndDropDatabaseRole.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/AddAndDropDatabaseRole.java) | -| Add Json Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonColumnSample.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/AddJsonColumnSample.java) | -| Add Jsonb Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonbColumnSample.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/AddJsonbColumnSample.java) | -| 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) | -| Create Instance With Autoscaling Config Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigExample.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/CreateInstanceWithAutoscalingConfigExample.java) | -| Create Instance With Processing Units Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsExample.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/CreateInstanceWithProcessingUnitsExample.java) | -| Create Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateSequenceSample.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/CreateSequenceSample.java) | -| Create 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/CreateTableWithForeignKeyDeleteCascadeSample.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/CreateTableWithForeignKeyDeleteCascadeSample.java) | -| Delete Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/DeleteInstanceConfigSample.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/DeleteInstanceConfigSample.java) | -| Drop Foreign Key Constraint Delete Cascade Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSample.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/DropForeignKeyConstraintDeleteCascadeSample.java) | -| Drop Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropSequenceSample.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/DropSequenceSample.java) | -| Enable Fine Grained Access | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/EnableFineGrainedAccess.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/EnableFineGrainedAccess.java) | -| Get Database Ddl Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetDatabaseDdlSample.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/GetDatabaseDdlSample.java) | -| Get Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetInstanceConfigSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/admin/generated/GetInstanceConfigSample.java) | -| List Database Roles | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabaseRoles.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/ListDatabaseRoles.java) | -| List Databases Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabasesSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabasesSample.java) | -| List Instance Config Operations Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigOperationsSample.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/ListInstanceConfigOperationsSample.java) | -| List Instance Configs Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigsSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigsSample.java) | -| Pg Alter Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgAlterSequenceSample.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/PgAlterSequenceSample.java) | -| Pg Case Sensitivity Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCaseSensitivitySample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCaseSensitivitySample.java) | -| 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) | +| Add And Drop Database Role | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/AddAndDropDatabaseRole.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/archived/AddAndDropDatabaseRole.java) | +| Add Json Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/AddJsonColumnSample.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/archived/AddJsonColumnSample.java) | +| Add Jsonb Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/AddJsonbColumnSample.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/archived/AddJsonbColumnSample.java) | +| Add Numeric Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/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/archived/AddNumericColumnSample.java) | +| Alter Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/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/archived/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/archived/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/archived/AlterTableWithForeignKeyDeleteCascadeSample.java) | +| Copy Backup Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/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/archived/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/archived/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/archived/CreateBackupWithEncryptionKey.java) | +| Create Database With Default Leader Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSample.java) | +| Create Database With Encryption Key | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/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/archived/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/archived/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/archived/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/archived/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/archived/CreateInstanceConfigSample.java) | +| Create Instance Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/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/archived/CreateInstanceExample.java) | +| Create Instance With Autoscaling Config Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigExample.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/archived/CreateInstanceWithAutoscalingConfigExample.java) | +| Create Instance With Processing Units Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithProcessingUnitsExample.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/archived/CreateInstanceWithProcessingUnitsExample.java) | +| Create Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateSequenceSample.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/archived/CreateSequenceSample.java) | +| Create 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/archived/CreateTableWithForeignKeyDeleteCascadeSample.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/archived/CreateTableWithForeignKeyDeleteCascadeSample.java) | +| Delete Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/DeleteInstanceConfigSample.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/archived/DeleteInstanceConfigSample.java) | +| Drop Foreign Key Constraint Delete Cascade Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/DropForeignKeyConstraintDeleteCascadeSample.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/archived/DropForeignKeyConstraintDeleteCascadeSample.java) | +| Drop Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/DropSequenceSample.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/archived/DropSequenceSample.java) | +| Enable Fine Grained Access | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/EnableFineGrainedAccess.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/archived/EnableFineGrainedAccess.java) | +| Get Database Ddl Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/GetDatabaseDdlSample.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/archived/GetDatabaseDdlSample.java) | +| Get Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/GetInstanceConfigSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/admin/archived/GetInstanceConfigSample.java) | +| List Database Roles | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListDatabaseRoles.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/archived/ListDatabaseRoles.java) | +| List Databases Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListDatabasesSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/admin/archived/ListDatabasesSample.java) | +| List Instance Config Operations Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListInstanceConfigOperationsSample.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/archived/ListInstanceConfigOperationsSample.java) | +| List Instance Configs Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListInstanceConfigsSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/admin/archived/ListInstanceConfigsSample.java) | +| Pg Alter Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/PgAlterSequenceSample.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/archived/PgAlterSequenceSample.java) | +| Pg Case Sensitivity Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/PgCaseSensitivitySample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/admin/archived/PgCaseSensitivitySample.java) | +| Pg Create Sequence Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/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/archived/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/archived/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/archived/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/archived/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/archived/PgInterleavedTableSample.java) | +| Pg Spanner Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/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/archived/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/archived/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/archived/RestoreBackupWithEncryptionKey.java) | +| Spanner Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/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/archived/SpannerSample.java) | +| Update Database Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/archived/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/archived/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/archived/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/archived/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/archived/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/archived/UpdateInstanceConfigSample.java) | diff --git a/samples/README.md b/samples/README.md index 9f961354d9..914c455b46 100644 --- a/samples/README.md +++ b/samples/README.md @@ -34,7 +34,7 @@ You can run a given `ClassName` via: ## Tutorial ### Running the tutorial - mvn exec:java -Dexec.mainClass=com.example.spanner.SpannerSample -Dexec.args=" my-instance my-database" + mvn exec:java -Dexec.mainClass=com.example.spanner.admin.archived.SpannerSample -Dexec.args=" my-instance my-database" ## Tracing sample `TracingSample.java` demonstrates how to export traces generated by client library to StackDriver and to /tracez page. diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index e8d2155b33..4103b4ad8e 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -165,7 +165,7 @@ false - com.example.spanner.SpannerSample + com.example.spanner.admin.archived.SpannerSample true lib/ diff --git a/samples/snippets/src/main/java/com/example/spanner/AddAndDropDatabaseRole.java b/samples/snippets/src/main/java/com/example/spanner/AddAndDropDatabaseRole.java index 2d65974099..75e85f5f85 100644 --- a/samples/snippets/src/main/java/com/example/spanner/AddAndDropDatabaseRole.java +++ b/samples/snippets/src/main/java/com/example/spanner/AddAndDropDatabaseRole.java @@ -17,12 +17,12 @@ package com.example.spanner; // [START spanner_add_and_drop_database_role] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.DatabaseAdminClient; + 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.UpdateDatabaseDdlMetadata; +import com.google.spanner.admin.database.v1.DatabaseName; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -34,54 +34,46 @@ static void addAndDropDatabaseRole() { String projectId = "my-project"; String instanceId = "my-instance"; String databaseId = "my-database"; - String parentRole = "my-new-parent-role"; - String childRole = "my-new-child-role"; + String parentRole = "parent_role"; + String childRole = "child_role"; addAndDropDatabaseRole(projectId, instanceId, databaseId, parentRole, childRole); } static void addAndDropDatabaseRole( - String projectId, String instanceId, String databaseId, String parentRole, String childRole) { + String projectId, String instanceId, String databaseId, + String parentRole, String childRole) { try (Spanner spanner = - SpannerOptions.newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - OperationFuture operation = - adminClient.updateDatabaseDdl( - instanceId, - databaseId, + 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), ImmutableList.of( - "CREATE ROLE " + parentRole, - "GRANT SELECT ON TABLE Albums TO ROLE " + parentRole, - "CREATE ROLE " + childRole, - "GRANT ROLE " + parentRole + " TO ROLE " + childRole), - null); - try { - System.out.println("Waiting for role create operation to complete..."); - operation.get(5, TimeUnit.MINUTES); - System.out.printf( - "Created roles %s and %s and granted privileges%n", parentRole, childRole); - // Delete role and membership. - operation = - adminClient.updateDatabaseDdl( - instanceId, - databaseId, - ImmutableList.of( - "REVOKE ROLE " + parentRole + " FROM ROLE " + childRole, - "DROP ROLE " + childRole), - null); - System.out.println("Waiting for role revoke & drop operation to complete..."); - operation.get(5, TimeUnit.MINUTES); - System.out.printf("Revoked privileges and dropped role %s%n", childRole); - } catch (ExecutionException | TimeoutException e) { - System.out.printf( - "Error: AddAndDropDatabaseRole failed with error message %s\n", e.getMessage()); - e.printStackTrace(); - } catch (InterruptedException e) { - System.out.println( - "Error: Waiting for AddAndDropDatabaseRole operation to finish was interrupted"); - } + String.format("CREATE ROLE %s", parentRole), + String.format("GRANT SELECT ON TABLE Albums TO ROLE %s", parentRole), + String.format("CREATE ROLE %s", childRole), + String.format("GRANT ROLE %s TO ROLE %s", parentRole, childRole))) + .get(5, TimeUnit.MINUTES); + System.out.printf( + "Created roles %s and %s and granted privileges%n", parentRole, childRole); + // Delete role and membership. + System.out.println("Waiting for role revoke & drop operation to complete..."); + databaseAdminClient.updateDatabaseDdlAsync( + DatabaseName.of(projectId, instanceId, databaseId), + ImmutableList.of( + String.format("REVOKE ROLE %s FROM ROLE %s", parentRole, childRole), + String.format("DROP ROLE %s", childRole))).get(5, TimeUnit.MINUTES); + System.out.printf("Revoked privileges and dropped role %s%n", childRole); + } catch (ExecutionException | TimeoutException e) { + System.out.printf( + "Error: AddAndDropDatabaseRole failed with error message %s\n", e.getMessage()); + e.printStackTrace(); + } catch (InterruptedException e) { + System.out.println( + "Error: Waiting for AddAndDropDatabaseRole operation to finish was interrupted"); } } } diff --git a/samples/snippets/src/main/java/com/example/spanner/AddJsonColumnSample.java b/samples/snippets/src/main/java/com/example/spanner/AddJsonColumnSample.java index 8be919ddad..c87b25ff47 100644 --- a/samples/snippets/src/main/java/com/example/spanner/AddJsonColumnSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/AddJsonColumnSample.java @@ -17,12 +17,12 @@ package com.example.spanner; // [START spanner_add_json_column] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.DatabaseAdminClient; + 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.UpdateDatabaseDdlMetadata; +import com.google.spanner.admin.database.v1.DatabaseName; import java.util.concurrent.ExecutionException; class AddJsonColumnSample { @@ -33,25 +33,24 @@ static void addJsonColumn() throws InterruptedException, ExecutionException { String instanceId = "my-instance"; String databaseId = "my-database"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - addJsonColumn(adminClient, instanceId, databaseId); - } + addJsonColumn(projectId, instanceId, databaseId); } - static void addJsonColumn(DatabaseAdminClient adminClient, String instanceId, String databaseId) + static void addJsonColumn(String projectId, String instanceId, String databaseId) throws InterruptedException, ExecutionException { - OperationFuture operation = - adminClient.updateDatabaseDdl( - instanceId, - databaseId, - ImmutableList.of("ALTER TABLE Venues ADD COLUMN VenueDetails JSON"), - null); - // Wait for the operation to finish. - // This will throw an ExecutionException if the operation fails. - operation.get(); - System.out.printf("Successfully added column `VenueDetails`%n"); + 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/AddJsonbColumnSample.java b/samples/snippets/src/main/java/com/example/spanner/AddJsonbColumnSample.java index e8152c4fa0..ab2607c498 100644 --- a/samples/snippets/src/main/java/com/example/spanner/AddJsonbColumnSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/AddJsonbColumnSample.java @@ -17,12 +17,12 @@ package com.example.spanner; // [START spanner_postgresql_jsonb_add_column] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.DatabaseAdminClient; + 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.UpdateDatabaseDdlMetadata; +import com.google.spanner.admin.database.v1.DatabaseName; import java.util.concurrent.ExecutionException; class AddJsonbColumnSample { @@ -33,25 +33,25 @@ static void addJsonbColumn() throws InterruptedException, ExecutionException { String instanceId = "my-instance"; String databaseId = "my-database"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - addJsonbColumn(adminClient, instanceId, databaseId); - } + addJsonbColumn(projectId, instanceId, databaseId); } - static void addJsonbColumn(DatabaseAdminClient adminClient, String instanceId, String databaseId) + static void addJsonbColumn(String projectId, String instanceId, String databaseId) throws InterruptedException, ExecutionException { - OperationFuture operation = - adminClient.updateDatabaseDdl( - instanceId, - databaseId, - ImmutableList.of("ALTER TABLE Venues ADD COLUMN VenueDetails JSONB"), - null); - // Wait for the operation to finish. - // This will throw an ExecutionException if the operation fails. - operation.get(); - System.out.printf("Successfully added column `VenueDetails`%n"); + 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/AddNumericColumnSample.java b/samples/snippets/src/main/java/com/example/spanner/AddNumericColumnSample.java index 7d29cdf15d..00cfb848e7 100644 --- a/samples/snippets/src/main/java/com/example/spanner/AddNumericColumnSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/AddNumericColumnSample.java @@ -17,12 +17,12 @@ package com.example.spanner; // [START spanner_add_numeric_column] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.DatabaseAdminClient; + 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.UpdateDatabaseDdlMetadata; +import com.google.spanner.admin.database.v1.DatabaseName; import java.util.concurrent.ExecutionException; class AddNumericColumnSample { @@ -33,26 +33,24 @@ static void addNumericColumn() throws InterruptedException, ExecutionException { String instanceId = "my-instance"; String databaseId = "my-database"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - addNumericColumn(adminClient, instanceId, databaseId); - } + addNumericColumn(projectId, instanceId, databaseId); } - static void addNumericColumn( - DatabaseAdminClient adminClient, String instanceId, String databaseId) + static void addNumericColumn(String projectId, String instanceId, String databaseId) throws InterruptedException, ExecutionException { - OperationFuture operation = - adminClient.updateDatabaseDdl( - instanceId, - databaseId, - ImmutableList.of("ALTER TABLE Venues ADD COLUMN Revenue NUMERIC"), - null); - // Wait for the operation to finish. - // This will throw an ExecutionException if the operation fails. - operation.get(); - System.out.printf("Successfully added column `Revenue`%n"); + 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/AlterSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/AlterSequenceSample.java index 6b9014a26f..641449ace9 100644 --- a/samples/snippets/src/main/java/com/example/spanner/AlterSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/AlterSequenceSample.java @@ -17,7 +17,7 @@ package com.example.spanner; // [START spanner_alter_sequence] -import com.google.cloud.spanner.DatabaseAdminClient; + import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ResultSet; @@ -25,13 +25,16 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; +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.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class AlterSequenceSample { + static void alterSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -42,17 +45,14 @@ static void alterSequence() { static void alterSequence(String projectId, String instanceId, String databaseId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { - dbAdminClient - .updateDatabaseDdl( - instanceId, - databaseId, + databaseAdminClient + .updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId, databaseId), ImmutableList.of( "ALTER SEQUENCE Seq SET OPTIONS " - + "(skip_range_min = 1000, skip_range_max = 5000000)"), - null) + + "(skip_range_min = 1000, skip_range_max = 5000000)")) .get(5, TimeUnit.MINUTES); System.out.println( diff --git a/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java index 1caf26fb28..6950e6a4fa 100644 --- a/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java @@ -17,10 +17,12 @@ package com.example.spanner; // [START spanner_alter_table_with_foreign_key_delete_cascade] -import com.google.cloud.spanner.DatabaseAdminClient; + 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; class AlterTableWithForeignKeyDeleteCascadeSample { @@ -30,30 +32,28 @@ static void alterForeignKeyDeleteCascadeConstraint() { String instanceId = "my-instance"; String databaseId = "my-database"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - alterForeignKeyDeleteCascadeConstraint(adminClient, instanceId, databaseId); - } + alterForeignKeyDeleteCascadeConstraint(projectId, instanceId, databaseId); } static void alterForeignKeyDeleteCascadeConstraint( - DatabaseAdminClient adminClient, String instanceId, String databaseId) { - adminClient.updateDatabaseDdl( - instanceId, - databaseId, - ImmutableList.of( - "ALTER TABLE ShoppingCarts\n" - + " ADD CONSTRAINT FKShoppingCartsCustomerName\n" - + " FOREIGN KEY (CustomerName)\n" - + " REFERENCES Customers(CustomerName)\n" - + " ON DELETE CASCADE\n"), - null); - 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/CopyBackupSample.java b/samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java index 795edcb15a..c6ee706687 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CopyBackupSample.java @@ -18,84 +18,80 @@ // [START spanner_copy_backup] -import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.Timestamp; -import com.google.cloud.spanner.Backup; -import com.google.cloud.spanner.BackupId; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; -import com.google.spanner.admin.database.v1.CopyBackupMetadata; -import java.time.LocalDateTime; +import 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.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() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; String instanceId = "my-instance"; String sourceBackupId = "my-backup"; String destinationBackupId = "my-destination-backup"; + try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { copyBackup(databaseAdminClient, projectId, instanceId, sourceBackupId, destinationBackupId); } } static void copyBackup( - DatabaseAdminClient databaseAdminClient, - String projectId, - String instanceId, - String sourceBackupId, - String destinationBackupId) { + DatabaseAdminClient databaseAdminClient, + String projectId, + String instanceId, + String sourceBackupId, + String destinationBackupId) { Timestamp expireTime = - Timestamp.ofTimeMicroseconds( - TimeUnit.MICROSECONDS.convert( - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), - TimeUnit.MILLISECONDS)); - // Creates a copy of an existing backup. - Backup destinationBackup = - databaseAdminClient - .newBackupBuilder(BackupId.of(projectId, instanceId, destinationBackupId)) - .setExpireTime(expireTime) - .build(); + 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 [" + destinationBackup.getId() + "]..."); - OperationFuture operation = - databaseAdminClient.copyBackup( - BackupId.of(projectId, instanceId, sourceBackupId), destinationBackup); + System.out.println("Copying backup [" + destinationBackupId + "]..."); + Backup destinationBackup; try { + // Creates a copy of an existing backup. // Wait for the backup operation to complete. - destinationBackup = operation.get(); - System.out.println("Copied backup [" + destinationBackup.getId() + "]"); + 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 = destinationBackup.reload(); + 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.getId().getName(), - destinationBackup.getSize(), - LocalDateTime.ofEpochSecond( - destinationBackup.getProto().getCreateTime().getSeconds(), - destinationBackup.getProto().getCreateTime().getNanos(), - OffsetDateTime.now().getOffset()), - LocalDateTime.ofEpochSecond( - destinationBackup.getProto().getVersionTime().getSeconds(), - destinationBackup.getProto().getVersionTime().getNanos(), - OffsetDateTime.now().getOffset()))); - return; + 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/CreateBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/CreateBackupWithEncryptionKey.java index b2a00ae2ad..e2c7b17061 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateBackupWithEncryptionKey.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateBackupWithEncryptionKey.java @@ -18,17 +18,18 @@ // [START spanner_create_backup_with_encryption_key] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.Timestamp; -import com.google.cloud.spanner.Backup; -import com.google.cloud.spanner.BackupId; -import com.google.cloud.spanner.DatabaseAdminClient; -import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; -import com.google.cloud.spanner.encryption.EncryptionConfigs; -import com.google.spanner.admin.database.v1.CreateBackupMetadata; +import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient; +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.CreateBackupRequest; +import com.google.spanner.admin.database.v1.DatabaseName; +import com.google.spanner.admin.database.v1.InstanceName; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -37,7 +38,7 @@ public class CreateBackupWithEncryptionKey { - static void createBackupWithEncryptionKey() throws InterruptedException { + static void createBackupWithEncryptionKey() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; String instanceId = "my-instance"; @@ -47,8 +48,8 @@ static void createBackupWithEncryptionKey() throws InterruptedException { "projects/" + projectId + "/locations//keyRings//cryptoKeys/"; try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) { createBackupWithEncryptionKey( adminClient, projectId, @@ -60,24 +61,29 @@ static void createBackupWithEncryptionKey() throws InterruptedException { } static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient, - String projectId, String instanceId, String databaseId, String backupId, String kmsKeyName) - throws InterruptedException { + String projectId, String instanceId, String databaseId, String backupId, String kmsKeyName) { // Set expire time to 14 days from now. - final Timestamp expireTime = Timestamp.ofTimeMicroseconds(TimeUnit.MICROSECONDS.convert( - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS)); - final Backup backupToCreate = adminClient - .newBackupBuilder(BackupId.of(projectId, instanceId, backupId)) - .setDatabase(DatabaseId.of(projectId, instanceId, databaseId)) - .setExpireTime(expireTime) - .setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName)) - .build(); - final OperationFuture operation = adminClient - .createBackup(backupToCreate); + 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(); - Backup backup; + 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 = operation.get(1200, TimeUnit.SECONDS); + 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()); @@ -89,14 +95,13 @@ static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient, // 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.getId().getName(), - backup.getSize(), + backup.getName(), + backup.getSizeBytes(), LocalDateTime.ofEpochSecond( - backup.getProto().getCreateTime().getSeconds(), - backup.getProto().getCreateTime().getNanos(), + backup.getCreateTime().getSeconds(), + backup.getCreateTime().getNanos(), OffsetDateTime.now().getOffset()), kmsKeyName ); diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSample.java b/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSample.java index fa3354dd9c..33917685cd 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSample.java @@ -1,11 +1,11 @@ /* - * Copyright 2021 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -18,60 +18,51 @@ //[START spanner_create_database_with_default_leader] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; -import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; -import com.google.cloud.spanner.SpannerOptions; -import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; -import java.util.Arrays; +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 java.io.IOException; import java.util.concurrent.ExecutionException; public class CreateDatabaseWithDefaultLeaderSample { - static void createDatabaseWithDefaultLeader() { + static void createDatabaseWithDefaultLeader() throws IOException { // TODO(developer): Replace these variables before running the sample. - final String projectId = "my-project"; - final String instanceId = "my-instance"; - final String databaseId = "my-database"; + final String instanceName = "projects/my-project/instances/my-instance-id"; + final String databaseId = "my-database-name"; final String defaultLeader = "my-default-leader"; - createDatabaseWithDefaultLeader(projectId, instanceId, databaseId, defaultLeader); + createDatabaseWithDefaultLeader(instanceName, databaseId, defaultLeader); } - static void createDatabaseWithDefaultLeader( - String projectId, String instanceId, String databaseId, String defaultLeader) { - try (Spanner spanner = SpannerOptions - .newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); - final OperationFuture operation = databaseAdminClient - .createDatabase( - instanceId, - databaseId, - Arrays.asList( - "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", - "ALTER DATABASE " + "`" + databaseId + "`" - + " SET OPTIONS ( default_leader = '" + defaultLeader + "' )" - ) - ); - final Database database = operation.get(); - System.out.println("Created database [" + database.getId() + "]"); - System.out.println("\tDefault leader: " + database.getDefaultLeader()); + static void createDatabaseWithDefaultLeader(String instanceName, String databaseId, + String defaultLeader) throws IOException { + try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) { + Database createdDatabase = + databaseAdminClient.createDatabaseAsync( + CreateDatabaseRequest.newBuilder() + .setParent(instanceName) + .setCreateStatement("CREATE DATABASE `" + databaseId + "`") + .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", + "ALTER DATABASE " + "`" + databaseId + "`" + + " SET OPTIONS ( default_leader = '" + defaultLeader + "' )")) + .build()).get(); + System.out.println("Created database [" + createdDatabase.getName() + "]"); + System.out.println("\tDefault leader: " + createdDatabase.getDefaultLeader()); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw (SpannerException) e.getCause(); @@ -82,4 +73,4 @@ static void createDatabaseWithDefaultLeader( } } } -//[END spanner_create_database_with_default_leader] +//[END spanner_create_database_with_default_leader] \ No newline at end of file diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithEncryptionKey.java index 7d3850933b..c06e9c3eba 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithEncryptionKey.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithEncryptionKey.java @@ -18,16 +18,15 @@ // [START spanner_create_database_with_encryption_key] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; -import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; -import com.google.cloud.spanner.encryption.EncryptionConfigs; -import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; -import java.util.Arrays; +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.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -43,8 +42,8 @@ static void createDatabaseWithEncryptionKey() { "projects/" + projectId + "/locations//keyRings//cryptoKeys/"; try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) { createDatabaseWithEncryptionKey( adminClient, projectId, @@ -56,32 +55,35 @@ static void createDatabaseWithEncryptionKey() { static void createDatabaseWithEncryptionKey(DatabaseAdminClient adminClient, String projectId, String instanceId, String databaseId, String kmsKeyName) { - final Database databaseToCreate = adminClient - .newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId)) - .setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(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(); - final OperationFuture operation = adminClient - .createDatabase(databaseToCreate, Arrays.asList( - "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" - )); try { System.out.println("Waiting for operation to complete..."); - Database createdDatabase = operation.get(120, TimeUnit.SECONDS); + Database createdDatabase = + adminClient.createDatabaseAsync(request).get(120, TimeUnit.SECONDS); System.out.printf( "Database %s created with encryption key %s%n", - createdDatabase.getId(), + createdDatabase.getName(), createdDatabase.getEncryptionConfig().getKmsKeyName() ); } catch (ExecutionException e) { diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithVersionRetentionPeriodSample.java b/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithVersionRetentionPeriodSample.java index 1eaa7c403d..888fe62588 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithVersionRetentionPeriodSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateDatabaseWithVersionRetentionPeriodSample.java @@ -18,15 +18,15 @@ // [START spanner_create_database_with_version_retention_period] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; -import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; -import java.util.Arrays; +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.util.concurrent.ExecutionException; public class CreateDatabaseWithVersionRetentionPeriodSample { @@ -38,39 +38,25 @@ static void createDatabaseWithVersionRetentionPeriod() { String databaseId = "my-database"; String versionRetentionPeriod = "7d"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - createDatabaseWithVersionRetentionPeriod(adminClient, instanceId, databaseId, - versionRetentionPeriod); - } + createDatabaseWithVersionRetentionPeriod(projectId, instanceId, databaseId, + versionRetentionPeriod); } - static void createDatabaseWithVersionRetentionPeriod(DatabaseAdminClient adminClient, + static void createDatabaseWithVersionRetentionPeriod(String projectId, String instanceId, String databaseId, String versionRetentionPeriod) { - OperationFuture op = - adminClient.createDatabase( - instanceId, - databaseId, - Arrays.asList( - "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", - "ALTER DATABASE " + "`" + databaseId + "`" - + " SET OPTIONS ( version_retention_period = '" + versionRetentionPeriod + "' )" - )); - try { - Database database = op.get(); - System.out.println("Created database [" + database.getId() + "]"); + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + CreateDatabaseRequest request = + CreateDatabaseRequest.newBuilder() + .setParent(InstanceName.of(projectId, instanceId).toString()) + .setCreateStatement("CREATE DATABASE `" + databaseId + "`") + .addAllExtraStatements(Lists.newArrayList("ALTER DATABASE " + "`" + databaseId + "`" + + " SET OPTIONS ( version_retention_period = '" + versionRetentionPeriod + "' )")) + .build(); + Database database = + databaseAdminClient.createDatabaseAsync(request).get(); + System.out.println("Created database [" + database.getName() + "]"); System.out.println("\tVersion retention period: " + database.getVersionRetentionPeriod()); System.out.println("\tEarliest version time: " + database.getEarliestVersionTime()); } catch (ExecutionException e) { diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceConfigSample.java index 379e0e2617..426d7c0484 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceConfigSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceConfigSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * 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. @@ -17,53 +17,73 @@ package com.example.spanner; // [START spanner_create_instance_config] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.InstanceConfig; -import com.google.cloud.spanner.InstanceConfigId; -import com.google.cloud.spanner.InstanceConfigInfo; -import com.google.cloud.spanner.ReplicaInfo; + import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; -import com.google.common.collect.ImmutableList; -import com.google.spanner.admin.instance.v1.CreateInstanceConfigMetadata; +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.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.Stream; class CreateInstanceConfigSample { + static void createInstanceConfig() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; - String baseInstanceConfig = "my-base-instance-config"; + String baseInstanceConfigId = "nam11"; String instanceConfigId = "custom-instance-config4"; - createInstanceConfig(projectId, baseInstanceConfig, instanceConfigId); + + createInstanceConfig(projectId, baseInstanceConfigId, instanceConfigId); } static void createInstanceConfig( - String projectId, String baseInstanceConfig, String instanceConfigId) { + String projectId, String baseInstanceConfigId, String instanceConfigId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); - final InstanceConfig baseConfig = instanceAdminClient.getInstanceConfig(baseInstanceConfig); - List readOnlyReplicas = - ImmutableList.of(baseConfig.getOptionalReplicas().get(0)); - InstanceConfigInfo instanceConfigInfo = - InstanceConfig.newBuilder(InstanceConfigId.of(projectId, instanceConfigId), baseConfig) - .setDisplayName(instanceConfigId) - .addReadOnlyReplicas(readOnlyReplicas) - .build(); - final OperationFuture operation = - instanceAdminClient.createInstanceConfig(instanceConfigInfo); + SpannerOptions.newBuilder() + .setProjectId(projectId) + .build() + .getService(); + InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) { + final InstanceConfigName baseInstanceConfigName = InstanceConfigName.of(projectId, + baseInstanceConfigId); + final InstanceConfig baseConfig = + instanceAdminClient.getInstanceConfig(baseInstanceConfigName.toString()); + final InstanceConfigName instanceConfigName = InstanceConfigName.of(projectId, + instanceConfigId); + /** + * The replicas for the custom instance configuration must include all the replicas of the + * base configuration, in addition to at least one from the list of optional replicas of the + * base configuration. + */ + final List replicas = + Stream.concat(baseConfig.getReplicasList().stream(), + baseConfig.getOptionalReplicasList().stream().limit(1)).collect(Collectors.toList()); + final InstanceConfig instanceConfig = + InstanceConfig.newBuilder().setName(instanceConfigName.toString()) + .setBaseConfig(baseInstanceConfigName.toString()) + .setDisplayName("Instance Configuration").addAllReplicas(replicas).build(); + final CreateInstanceConfigRequest createInstanceConfigRequest = + CreateInstanceConfigRequest.newBuilder().setParent(ProjectName.of(projectId).toString()) + .setInstanceConfigId(instanceConfigId).setInstanceConfig(instanceConfig).build(); try { - System.out.printf("Waiting for create operation for %s to complete...\n", instanceConfigId); - InstanceConfig instanceConfig = operation.get(5, TimeUnit.MINUTES); - System.out.printf("Created instance configuration %s\n", instanceConfig.getId()); + System.out.printf("Waiting for create operation for %s to complete...\n", + instanceConfigName); + InstanceConfig instanceConfigResult = + instanceAdminClient.createInstanceConfigAsync( + createInstanceConfigRequest).get(5, TimeUnit.MINUTES); + System.out.printf("Created instance configuration %s\n", instanceConfigResult.getName()); } catch (ExecutionException | TimeoutException e) { System.out.printf( "Error: Creating instance configuration %s failed with error message %s\n", - instanceConfigInfo.getId(), e.getMessage()); + instanceConfig.getName(), e.getMessage()); } catch (InterruptedException e) { System.out.println( "Error: Waiting for createInstanceConfig operation to finish was interrupted"); diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceExample.java b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceExample.java index 44e36a8b27..b53727ba6d 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,14 @@ package com.example.spanner; //[START spanner_create_instance] -import com.google.api.gax.longrunning.OperationFuture; -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.Spanner; import com.google.cloud.spanner.SpannerOptions; -import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; +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.util.concurrent.ExecutionException; class CreateInstanceExample { @@ -38,36 +37,41 @@ static void createInstance() { } static void createInstance(String projectId, String instanceId) { - Spanner spanner = SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); - // Set Instance configuration. - String configId = "regional-us-central1"; int nodeCount = 2; String displayName = "Descriptive name"; - // Create an InstanceInfo object that will be used to create the instance. - InstanceInfo instanceInfo = - InstanceInfo.newBuilder(InstanceId.of(projectId, instanceId)) - .setInstanceConfigId(InstanceConfigId.of(projectId, configId)) - .setNodeCount(nodeCount) + // Create an Instance object that will be used to create the instance. + Instance instance = + Instance.newBuilder() .setDisplayName(displayName) + .setNodeCount(nodeCount) + .setConfig( + InstanceConfigName.of(projectId, "regional-us-central1").toString()) .build(); - OperationFuture operation = - instanceAdminClient.createInstance(instanceInfo); - try { + + try (Spanner spanner = + SpannerOptions.newBuilder() + .setProjectId(projectId) + .build() + .getService(); + InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) { + // Wait for the createInstance operation to finish. - Instance instance = operation.get(); - System.out.printf("Instance %s was successfully created%n", instance.getId()); + Instance createdInstance = instanceAdminClient.createInstanceAsync( + CreateInstanceRequest.newBuilder() + .setParent(ProjectName.of(projectId).toString()) + .setInstanceId(instanceId) + .setInstance(instance) + .build()).get(); + System.out.printf("Instance %s was successfully created%n", createdInstance.getName()); } catch (ExecutionException e) { System.out.printf( "Error: Creating instance %s failed with error message %s%n", - instanceInfo.getId(), e.getMessage()); + instance.getName(), e.getMessage()); } catch (InterruptedException e) { System.out.println("Error: Waiting for createInstance operation to finish was interrupted"); - } finally { - spanner.close(); } } } -//[END spanner_create_instance] +//[END spanner_create_instance] \ No newline at end of file diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithAutoscalingConfigExample.java b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithAutoscalingConfigExample.java index 0719857411..dc62dd7a68 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithAutoscalingConfigExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithAutoscalingConfigExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Google LLC + * 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. @@ -18,16 +18,14 @@ // [START spanner_create_instance_with_autoscaling_config] -import com.google.api.gax.longrunning.OperationFuture; -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.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.CreateInstanceMetadata; +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.util.concurrent.ExecutionException; class CreateInstanceWithAutoscalingConfigExample { @@ -40,44 +38,55 @@ static void createInstance() { } static void createInstance(String projectId, String instanceId) { - Spanner spanner = SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + 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"; - // Set Instance configuration. - String configId = "regional-us-central1"; - // Create an autoscaling config. - AutoscalingConfig autoscalingConfig = - AutoscalingConfig.newBuilder() - .setAutoscalingLimits( - AutoscalingConfig.AutoscalingLimits.newBuilder().setMinNodes(1).setMaxNodes(2)) - .setAutoscalingTargets( - AutoscalingConfig.AutoscalingTargets.newBuilder() - .setHighPriorityCpuUtilizationPercent(65) - .setStorageUtilizationPercent(95)) - .build(); + // Create an autoscaling config. + // When autoscaling_config is enabled, node_count and processing_units fields + // need not be specified. + AutoscalingConfig autoscalingConfig = + AutoscalingConfig.newBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder().setMinNodes(1).setMaxNodes(2)) + .setAutoscalingTargets( + AutoscalingConfig.AutoscalingTargets.newBuilder() + .setHighPriorityCpuUtilizationPercent(65) + .setStorageUtilizationPercent(95)) + .build(); + Instance instance = + Instance.newBuilder() + .setAutoscalingConfig(autoscalingConfig) + .setDisplayName(displayName) + .setConfig( + InstanceConfigName.of(projectId, configId).toString()) + .build(); - // Create an InstanceInfo object that will be used to create the instance. - InstanceInfo instanceInfo = - InstanceInfo.newBuilder(InstanceId.of(projectId, instanceId)) - .setInstanceConfigId(InstanceConfigId.of(projectId, configId)) - .setAutoscalingConfig(autoscalingConfig) - .setDisplayName("Descriptive name") - .build(); - OperationFuture operation = - instanceAdminClient.createInstance(instanceInfo); - - try { - // Wait for the createInstance operation to finish. - Instance instance = operation.get(); - System.out.printf("Autoscaler instance %s was successfully created%n", instance.getId()); - } catch (ExecutionException e) { - System.out.printf( - "Error: Creating instance %s failed with error message %s%n", - instanceInfo.getId(), e.getMessage()); - } catch (InterruptedException e) { - System.out.println("Error: Waiting for createInstance operation to finish was interrupted"); - } finally { - spanner.close(); + // Creates a new instance + System.out.printf("Creating instance %s.%n", instanceId); + try { + // Wait for the createInstance operation to finish. + Instance instanceResult = instanceAdminClient.createInstanceAsync( + CreateInstanceRequest.newBuilder() + .setParent(ProjectName.of(projectId).toString()) + .setInstanceId(instanceId) + .setInstance(instance) + .build()).get(); + System.out.printf("Autoscaler instance %s was successfully created%n", + instanceResult.getName()); + } catch (ExecutionException e) { + System.out.printf( + "Error: Creating instance %s failed with error message %s%n", + instance.getName(), e.getMessage()); + } catch (InterruptedException e) { + System.out.println("Error: Waiting for createInstance operation to finish was interrupted"); + } } } } diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithProcessingUnitsExample.java b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithProcessingUnitsExample.java index 1bfc66d3fd..293c10249c 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithProcessingUnitsExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithProcessingUnitsExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Google LLC + * 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. @@ -18,15 +18,13 @@ //[START spanner_create_instance_with_processing_units] -import com.google.api.gax.longrunning.OperationFuture; -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.Spanner; import com.google.cloud.spanner.SpannerOptions; -import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; +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; class CreateInstanceWithProcessingUnitsExample { @@ -38,39 +36,45 @@ static void createInstance() { } static void createInstance(String projectId, String instanceId) { - Spanner spanner = SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + 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. - int processingUnits = 500; - String displayName = "Descriptive name"; + // Set Instance configuration. + String configId = "regional-us-central1"; + // This will create an instance with the processing power of 0.2 nodes. + int processingUnits = 500; + String displayName = "Descriptive name"; - try { - // Creates a new instance - System.out.printf("Creating instance %s.%n", instanceId); - OperationFuture operation = - instanceAdminClient.createInstance(InstanceInfo - .newBuilder(InstanceId.of(projectId, instanceId)) - .setInstanceConfigId(InstanceConfigId.of(projectId, configId)) - .setProcessingUnits(processingUnits) - .setDisplayName(displayName) - .build()); + try { + // Creates a new instance + System.out.printf("Creating instance %s.%n", instanceId); + Instance instance = + Instance.newBuilder() + .setDisplayName(displayName) + .setProcessingUnits(processingUnits) + .setConfig( + InstanceConfigName.of(projectId, configId).toString()) + .build(); + // Wait for the createInstance operation to finish. + System.out.printf("Waiting for operation on %s to complete...%n", instanceId); + Instance createdInstance = instanceAdminClient.createInstanceAsync( + CreateInstanceRequest.newBuilder() + .setParent(ProjectName.of(projectId).toString()) + .setInstanceId(instanceId) + .setInstance(instance) + .build()).get(); - // Wait for the createInstance operation to finish. - System.out.printf("Waiting for operation on %s to complete...%n", instanceId); - Instance createdInstance = operation.get(); - - System.out.printf("Created instance %s.%n", createdInstance.getId().getInstance()); - - Instance instance = instanceAdminClient.getInstance(instanceId); - System.out.printf("Instance %s has %d processing units.%n", instance.getId().getInstance(), - instance.getProcessingUnits()); - } catch (Exception e) { - System.out.printf("Error: %s.%n", e.getMessage()); + System.out.printf("Created instance %s.%n", createdInstance.getName()); + System.out.printf("Instance %s has %d processing units.%n", createdInstance.getName(), + createdInstance.getProcessingUnits()); + } catch (Exception e) { + System.out.printf("Error: %s.%n", e.getMessage()); + } } - spanner.close(); } } //[END spanner_create_instance_with_processing_units] diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/CreateSequenceSample.java index 964b245ed8..757921080d 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateSequenceSample.java @@ -17,7 +17,7 @@ package com.example.spanner; // [START spanner_create_sequence] -import com.google.cloud.spanner.DatabaseAdminClient; + import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ResultSet; @@ -25,13 +25,16 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; +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.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class CreateSequenceSample { + static void createSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -41,20 +44,18 @@ static void createSequence() { } static void createSequence(String projectId, String instanceId, String databaseId) { - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - dbAdminClient - .updateDatabaseDdl( - instanceId, - databaseId, + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + databaseAdminClient + .updateDatabaseDdlAsync( + DatabaseName.of(projectId, instanceId, databaseId), ImmutableList.of( "CREATE SEQUENCE Seq OPTIONS (sequence_kind = 'bit_reversed_positive')", "CREATE TABLE Customers (CustomerId INT64 DEFAULT " + "(GET_NEXT_SEQUENCE_VALUE(SEQUENCE Seq)), CustomerName STRING(1024)) " - + "PRIMARY KEY (CustomerId)"), - null) + + "PRIMARY KEY (CustomerId)")) .get(5, TimeUnit.MINUTES); System.out.println( diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java index dda09591ee..c9484916a4 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java @@ -17,10 +17,12 @@ package com.example.spanner; // [START spanner_create_table_with_foreign_key_delete_cascade] -import com.google.cloud.spanner.DatabaseAdminClient; + 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; class CreateTableWithForeignKeyDeleteCascadeSample { @@ -30,37 +32,35 @@ static void createForeignKeyDeleteCascadeConstraint() { String instanceId = "my-instance"; String databaseId = "my-database"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - createForeignKeyDeleteCascadeConstraint(adminClient, instanceId, databaseId); - } + createForeignKeyDeleteCascadeConstraint(projectId, instanceId, databaseId); } static void createForeignKeyDeleteCascadeConstraint( - DatabaseAdminClient adminClient, String instanceId, String databaseId) { - adminClient.updateDatabaseDdl( - 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"), - null); + 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)); + 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/DeleteInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/DeleteInstanceConfigSample.java index de76673ca7..c2da7b3000 100644 --- a/samples/snippets/src/main/java/com/example/spanner/DeleteInstanceConfigSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/DeleteInstanceConfigSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * 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. @@ -17,12 +17,16 @@ package com.example.spanner; // [START spanner_delete_instance_config] -import com.google.cloud.spanner.InstanceAdminClient; + 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; class DeleteInstanceConfigSample { + static void deleteInstanceConfig() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; @@ -32,16 +36,24 @@ static void deleteInstanceConfig() { static void deleteInstanceConfig(String projectId, String instanceConfigId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + SpannerOptions.newBuilder() + .setProjectId(projectId) + .build() + .getService(); + InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) { + final InstanceConfigName instanceConfigName = InstanceConfigName.of(projectId, + instanceConfigId); + final DeleteInstanceConfigRequest request = + DeleteInstanceConfigRequest.newBuilder().setName(instanceConfigName.toString()).build(); + try { - System.out.printf("Deleting %s...\n", instanceConfigId); - instanceAdminClient.deleteInstanceConfig(instanceConfigId); - System.out.printf("Deleted instance configuration %s\n", instanceConfigId); + System.out.printf("Deleting %s...\n", instanceConfigName); + instanceAdminClient.deleteInstanceConfig(request); + System.out.printf("Deleted instance configuration %s\n", instanceConfigName); } catch (SpannerException e) { System.out.printf( "Error: Deleting instance configuration %s failed with error message: %s\n", - instanceConfigId, e.getMessage()); + instanceConfigName, e.getMessage()); } } } diff --git a/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java index 13f39d129f..7c35b9f621 100644 --- a/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java @@ -17,10 +17,12 @@ package com.example.spanner; // [START spanner_drop_foreign_key_constraint_delete_cascade] -import com.google.cloud.spanner.DatabaseAdminClient; + 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; class DropForeignKeyConstraintDeleteCascadeSample { @@ -30,28 +32,26 @@ static void deleteForeignKeyDeleteCascadeConstraint() { String instanceId = "my-instance"; String databaseId = "my-database"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - deleteForeignKeyDeleteCascadeConstraint(adminClient, instanceId, databaseId); - } + deleteForeignKeyDeleteCascadeConstraint(projectId, instanceId, databaseId); } static void deleteForeignKeyDeleteCascadeConstraint( - DatabaseAdminClient adminClient, String instanceId, String databaseId) { - adminClient.updateDatabaseDdl( - instanceId, - databaseId, - ImmutableList.of( - "ALTER TABLE ShoppingCarts\n" - + " DROP CONSTRAINT FKShoppingCartsCustomerName\n"), - null); - - 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/DropSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/DropSequenceSample.java index 6d054eea4d..9f1b32caed 100644 --- a/samples/snippets/src/main/java/com/example/spanner/DropSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/DropSequenceSample.java @@ -17,16 +17,19 @@ package com.example.spanner; // [START spanner_drop_sequence] -import com.google.cloud.spanner.DatabaseAdminClient; + 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.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class DropSequenceSample { + static void dropSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -37,19 +40,14 @@ static void dropSequence() { static void dropSequence(String projectId, String instanceId, String databaseId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - - dbAdminClient - .updateDatabaseDdl( - instanceId, - databaseId, + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + databaseAdminClient + .updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId, databaseId), ImmutableList.of( "ALTER TABLE Customers ALTER COLUMN CustomerId DROP DEFAULT", - "DROP SEQUENCE Seq"), - null) + "DROP SEQUENCE Seq")) .get(5, TimeUnit.MINUTES); - System.out.println( "Altered Customers table to drop DEFAULT from CustomerId column " + "and dropped the Seq sequence"); diff --git a/samples/snippets/src/main/java/com/example/spanner/EnableFineGrainedAccess.java b/samples/snippets/src/main/java/com/example/spanner/EnableFineGrainedAccess.java index c4c17645b6..e4e35bd95a 100644 --- a/samples/snippets/src/main/java/com/example/spanner/EnableFineGrainedAccess.java +++ b/samples/snippets/src/main/java/com/example/spanner/EnableFineGrainedAccess.java @@ -17,13 +17,18 @@ package com.example.spanner; // [START spanner_enable_fine_grained_access] -import com.google.cloud.Binding; -import com.google.cloud.Condition; -import com.google.cloud.Policy; -import com.google.cloud.spanner.DatabaseAdminClient; + 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; +import com.google.iam.v1.GetIamPolicyRequest; +import com.google.iam.v1.GetPolicyOptions; +import com.google.iam.v1.Policy; +import com.google.iam.v1.SetIamPolicyRequest; +import com.google.spanner.admin.database.v1.DatabaseName; +import com.google.type.Expr; public class EnableFineGrainedAccess { @@ -46,12 +51,15 @@ static void enableFineGrainedAccess( String title, String role) { try (Spanner spanner = - SpannerOptions.newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - Policy policy = adminClient.getDatabaseIAMPolicy(instanceId, databaseId, 3); + 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 @@ -65,20 +73,17 @@ static void enableFineGrainedAccess( Binding binding1 = Binding.newBuilder() .setRole("roles/spanner.fineGrainedAccessUser") - .setMembers(ImmutableList.of(iamMember)) + .addAllMembers(ImmutableList.of(iamMember)) .build(); Binding binding2 = Binding.newBuilder() .setRole("roles/spanner.databaseRoleUser") .setCondition( - Condition.newBuilder() - .setDescription(title) - .setExpression( - String.format("resource.name.endsWith(\"/databaseRoles/%s\")", role)) - .setTitle(title) - .build()) - .setMembers(ImmutableList.of(iamMember)) + 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() @@ -90,10 +95,13 @@ static void enableFineGrainedAccess( Policy.newBuilder() .setVersion(policyVersion) .setEtag(policy.getEtag()) - .setBindings(bindings) + .addAllBindings(bindings) .build(); - Policy response = - adminClient.setDatabaseIAMPolicy(instanceId, databaseId, policyWithConditions); + 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()); } diff --git a/samples/snippets/src/main/java/com/example/spanner/GetDatabaseDdlSample.java b/samples/snippets/src/main/java/com/example/spanner/GetDatabaseDdlSample.java index 93d32a5ec6..b84f1c0ccc 100644 --- a/samples/snippets/src/main/java/com/example/spanner/GetDatabaseDdlSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/GetDatabaseDdlSample.java @@ -18,10 +18,11 @@ //[START spanner_get_database_ddl] -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; -import java.util.List; +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; public class GetDatabaseDdlSample { @@ -35,15 +36,13 @@ static void getDatabaseDdl() { static void getDatabaseDdl( String projectId, String instanceId, String databaseId) { - try (Spanner spanner = SpannerOptions - .newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); - final List ddls = databaseAdminClient.getDatabaseDdl(instanceId, 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 : ddls) { + for (String ddl : response.getStatementsList()) { System.out.println(ddl); } } diff --git a/samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java index 46f38b84d1..9dd8690f75 100644 --- a/samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Google LLC + * 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. @@ -18,35 +18,38 @@ //[START spanner_get_instance_config] -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.InstanceConfig; 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; public class GetInstanceConfigSample { static void getInstanceConfig() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; - final String instanceConfigName = "nam6"; - getInstanceConfig(projectId, instanceConfigName); + final String instanceConfigId = "nam6"; + getInstanceConfig(projectId, instanceConfigId); } - static void getInstanceConfig(String projectId, String instanceConfigName) { - try (Spanner spanner = SpannerOptions - .newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + 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); - final InstanceConfig instanceConfig = instanceAdminClient - .getInstanceConfig(instanceConfigName); + final InstanceConfig instanceConfig = + instanceAdminClient.getInstanceConfig(instanceConfigName.toString()); System.out.printf( "Available leader options for instance config %s: %s%n", - instanceConfig.getId(), - instanceConfig.getLeaderOptions() + instanceConfig.getName(), + instanceConfig.getLeaderOptionsList() ); } } diff --git a/samples/snippets/src/main/java/com/example/spanner/ListDatabaseRoles.java b/samples/snippets/src/main/java/com/example/spanner/ListDatabaseRoles.java index 9292405054..e16a55cb7b 100644 --- a/samples/snippets/src/main/java/com/example/spanner/ListDatabaseRoles.java +++ b/samples/snippets/src/main/java/com/example/spanner/ListDatabaseRoles.java @@ -17,16 +17,18 @@ package com.example.spanner; // [START spanner_list_database_roles] -import com.google.cloud.spanner.DatabaseAdminClient; -import com.google.cloud.spanner.DatabaseId; -import com.google.cloud.spanner.DatabaseRole; + import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; -import java.util.concurrent.ExecutionException; +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; public class ListDatabaseRoles { - static void listDatabaseRoles() throws InterruptedException, ExecutionException { + static void listDatabaseRoles() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; String instanceId = "my-instance"; @@ -36,15 +38,16 @@ static void listDatabaseRoles() throws InterruptedException, ExecutionException static void listDatabaseRoles(String projectId, String instanceId, String databaseId) { try (Spanner spanner = - SpannerOptions.newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); - String databasePath = DatabaseId.of(projectId, instanceId, databaseId).getName(); + 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 (DatabaseRole role : adminClient.listDatabaseRoles(instanceId, databaseId).iterateAll()) { - System.out.printf("%s%n", role.getName()); + 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/ListDatabasesSample.java b/samples/snippets/src/main/java/com/example/spanner/ListDatabasesSample.java index 6e6a246a30..631b72dc11 100644 --- a/samples/snippets/src/main/java/com/example/spanner/ListDatabasesSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/ListDatabasesSample.java @@ -18,11 +18,13 @@ //[START spanner_list_databases] -import com.google.api.gax.paging.Page; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; 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; public class ListDatabasesSample { @@ -34,21 +36,20 @@ static void listDatabases() { } static void listDatabases(String projectId, String instanceId) { - try (Spanner spanner = SpannerOptions - .newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); - Page page = databaseAdminClient.listDatabases(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); - while (page != null) { + + 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.getId() + " " + defaultLeader); + System.out.println("\t" + database.getName() + " " + defaultLeader); } - page = page.getNextPage(); } } } diff --git a/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigOperationsSample.java b/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigOperationsSample.java index bcf68b7d58..b42c52126b 100644 --- a/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigOperationsSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigOperationsSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * 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. @@ -17,15 +17,18 @@ package com.example.spanner; // [START spanner_list_instance_config_operations] -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.Options; + 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; public class ListInstanceConfigOperationsSample { + static void listInstanceConfigOperations() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; @@ -34,32 +37,36 @@ static void listInstanceConfigOperations() { static void listInstanceConfigOperations(String projectId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); - - try { - System.out.printf( - "Getting list of instance config operations for project %s...\n", - projectId); - final Iterable instanceConfigOperations = - instanceAdminClient - .listInstanceConfigOperations( - Options.filter( - "(metadata.@type=type.googleapis.com/" - + "google.spanner.admin.instance.v1.CreateInstanceConfigMetadata)")) - .iterateAll(); - for (Operation operation : instanceConfigOperations) { - CreateInstanceConfigMetadata metadata = - operation.getMetadata().unpack(CreateInstanceConfigMetadata.class); - System.out.printf( - "Create instance config operation for %s is %d%% completed.\n", - metadata.getInstanceConfig().getName(), metadata.getProgress().getProgressPercent()); - } - } catch (InvalidProtocolBufferException e) { + 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", + projectId); + final ListInstanceConfigOperationsRequest request = + ListInstanceConfigOperationsRequest.newBuilder() + .setParent(projectName.toString()) + .setFilter("(metadata.@type=type.googleapis.com/" + + "google.spanner.admin.instance.v1.CreateInstanceConfigMetadata)").build(); + final Iterable instanceConfigOperations = + instanceAdminClient.listInstanceConfigOperations(request).iterateAll(); + for (Operation operation : instanceConfigOperations) { + CreateInstanceConfigMetadata metadata = + operation.getMetadata().unpack(CreateInstanceConfigMetadata.class); System.out.printf( - "Error: Listing instance config operations failed with error message %s\n", - e.getMessage()); + "Create instance config operation for %s is %d%% completed.\n", + metadata.getInstanceConfig().getName(), metadata.getProgress().getProgressPercent()); } + System.out.printf( + "Obtained list of instance config operations for project %s...\n", + projectName); + } catch (InvalidProtocolBufferException e) { + System.out.printf( + "Error: Listing instance config operations failed with error message %s\n", + e.getMessage()); } } } diff --git a/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java b/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java index d0de12dfce..7c5391638b 100644 --- a/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Google LLC + * 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. @@ -18,32 +18,34 @@ //[START spanner_list_instance_configs] -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.InstanceConfig; 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; public class ListInstanceConfigsSample { static void listInstanceConfigs() { // TODO(developer): Replace these variables before running the sample. - final String projectId = "my-project"; + String projectId = "my-project"; listInstanceConfigs(projectId); } static void listInstanceConfigs(String projectId) { - try (Spanner spanner = SpannerOptions - .newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); - - for (InstanceConfig instanceConfig : instanceAdminClient.listInstanceConfigs().iterateAll()) { + 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()) { System.out.printf( "Available leader options for instance config %s: %s%n", - instanceConfig.getId(), - instanceConfig.getLeaderOptions() + instanceConfig.getName(), + instanceConfig.getLeaderOptionsList() ); } } diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAlterSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/PgAlterSequenceSample.java index e10c29807f..a3e4a9a677 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgAlterSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgAlterSequenceSample.java @@ -17,7 +17,7 @@ package com.example.spanner; // [START spanner_postgresql_alter_sequence] -import com.google.cloud.spanner.DatabaseAdminClient; + import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ResultSet; @@ -25,13 +25,16 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; +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.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class PgAlterSequenceSample { + static void pgAlterSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -42,14 +45,13 @@ static void pgAlterSequence() { static void pgAlterSequence(String projectId, String instanceId, String databaseId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - dbAdminClient - .updateDatabaseDdl( - instanceId, - databaseId, - ImmutableList.of("ALTER SEQUENCE Seq SKIP RANGE 1000 5000000"), - null) + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + + databaseAdminClient + .updateDatabaseDdlAsync( + DatabaseName.of(projectId, instanceId, databaseId), + ImmutableList.of("ALTER SEQUENCE Seq SKIP RANGE 1000 5000000")) .get(5, TimeUnit.MINUTES); System.out.println( "Altered Seq sequence to skip an inclusive range between 1000 and 5000000"); diff --git a/samples/snippets/src/main/java/com/example/spanner/PgCaseSensitivitySample.java b/samples/snippets/src/main/java/com/example/spanner/PgCaseSensitivitySample.java index d76b26b178..abebdef39a 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgCaseSensitivitySample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgCaseSensitivitySample.java @@ -18,8 +18,6 @@ // [START spanner_postgresql_identifier_case_sensitivity] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Mutation; @@ -28,7 +26,9 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; -import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +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.util.Collections; import java.util.concurrent.ExecutionException; @@ -43,34 +43,28 @@ static void pgCaseSensitivity() { } static void pgCaseSensitivity(String projectId, String instanceId, String databaseId) { + try (Spanner spanner = - SpannerOptions.newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + 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. // 2. Identifiers that are double-quoted retain their case and are case-sensitive. // See https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS // for more information. - final OperationFuture updateOperation = - databaseAdminClient.updateDatabaseDdl( - instanceId, - databaseId, - Collections.singleton( - "CREATE TABLE Singers (" - // SingerId will be folded to `singerid`. - + " SingerId bigint NOT NULL PRIMARY KEY," - // FirstName and LastName are double-quoted and will therefore retain their - // mixed case and are case-sensitive. This means that any statement that - // references any of these columns must use double quotes. - + " \"FirstName\" varchar(1024) NOT NULL," - + " \"LastName\" varchar(1024) NOT NULL" - + ")"), - null); - updateOperation.get(); + databaseAdminClient.updateDatabaseDdlAsync( + DatabaseName.of(projectId, instanceId, databaseId), + Lists.newArrayList( + "CREATE TABLE Singers (" + // SingerId will be folded to `singerid`. + + " SingerId bigint NOT NULL PRIMARY KEY," + // FirstName and LastName are double-quoted and will therefore retain their + // mixed case and are case-sensitive. This means that any statement that + // references any of these columns must use double quotes. + + " \"FirstName\" varchar(1024) NOT NULL," + + " \"LastName\" varchar(1024) NOT NULL" + + ")")).get(); DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); diff --git a/samples/snippets/src/main/java/com/example/spanner/PgCreateSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/PgCreateSequenceSample.java index e6e23f49b8..79445aa272 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgCreateSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgCreateSequenceSample.java @@ -17,7 +17,7 @@ package com.example.spanner; // [START spanner_postgresql_create_sequence] -import com.google.cloud.spanner.DatabaseAdminClient; + import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ResultSet; @@ -25,13 +25,16 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; +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.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class PgCreateSequenceSample { + static void pgCreateSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -42,22 +45,18 @@ static void pgCreateSequence() { static void pgCreateSequence(String projectId, String instanceId, String databaseId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - - dbAdminClient - .updateDatabaseDdl( - instanceId, - databaseId, + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + databaseAdminClient + .updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId, databaseId).toString(), ImmutableList.of( "CREATE SEQUENCE Seq BIT_REVERSED_POSITIVE;", "CREATE TABLE Customers (CustomerId BIGINT DEFAULT nextval('Seq'), " - + "CustomerName character varying(1024), PRIMARY KEY (CustomerId))"), - null) + + "CustomerName character varying(1024), PRIMARY KEY (CustomerId))")) .get(5, TimeUnit.MINUTES); System.out.println( - "Created Seq sequence and Customers table, where its key column " + "Created Seq sequence and Customers table, where the key column " + "CustomerId uses the sequence as a default value"); final DatabaseClient dbClient = diff --git a/samples/snippets/src/main/java/com/example/spanner/PgDropSequenceSample.java b/samples/snippets/src/main/java/com/example/spanner/PgDropSequenceSample.java index c0990c1c4d..129009e9b2 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgDropSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgDropSequenceSample.java @@ -17,16 +17,19 @@ package com.example.spanner; // [START spanner_postgresql_drop_sequence] -import com.google.cloud.spanner.DatabaseAdminClient; + 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.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class PgDropSequenceSample { + static void pgDropSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -36,17 +39,16 @@ static void pgDropSequence() { } static void pgDropSequence(String projectId, String instanceId, String databaseId) { + try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - dbAdminClient - .updateDatabaseDdl( - instanceId, - databaseId, + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + databaseAdminClient + .updateDatabaseDdlAsync( + DatabaseName.of(projectId, instanceId, databaseId), ImmutableList.of( "ALTER TABLE Customers ALTER COLUMN CustomerId DROP DEFAULT", - "DROP SEQUENCE Seq"), - null) + "DROP SEQUENCE Seq")) .get(5, TimeUnit.MINUTES); System.out.println( "Altered Customers table to drop DEFAULT from " diff --git a/samples/snippets/src/main/java/com/example/spanner/PgInterleavedTableSample.java b/samples/snippets/src/main/java/com/example/spanner/PgInterleavedTableSample.java index d79ba7b58f..30ee48ed6d 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgInterleavedTableSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgInterleavedTableSample.java @@ -18,12 +18,11 @@ // [START spanner_postgresql_interleaved_table] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; -import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient; +import com.google.spanner.admin.database.v1.DatabaseName; import java.util.Arrays; import java.util.concurrent.ExecutionException; @@ -38,35 +37,29 @@ static void pgInterleavedTable() { } static void pgInterleavedTable(String projectId, String instanceId, String databaseId) { - try (Spanner spanner = - SpannerOptions.newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + 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 // for the full CREATE TABLE syntax. - final OperationFuture updateOperation = - databaseAdminClient.updateDatabaseDdl( + databaseAdminClient.updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId, - databaseId, - Arrays.asList( - "CREATE TABLE Singers (" - + " SingerId bigint NOT NULL PRIMARY KEY," - + " FirstName varchar(1024) NOT NULL," - + " LastName varchar(1024) NOT NULL" - + ")", - "CREATE TABLE Albums (" - + " SingerId bigint NOT NULL," - + " AlbumId bigint NOT NULL," - + " Title varchar(1024) NOT NULL," - + " PRIMARY KEY (SingerId, AlbumId)" - + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), - null); - updateOperation.get(); + databaseId), + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL PRIMARY KEY," + + " FirstName varchar(1024) NOT NULL," + + " LastName varchar(1024) NOT NULL" + + ")", + "CREATE TABLE Albums (" + + " SingerId bigint NOT NULL," + + " AlbumId bigint NOT NULL," + + " Title varchar(1024) NOT NULL," + + " PRIMARY KEY (SingerId, AlbumId)" + + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE")).get(); System.out.println("Created interleaved table hierarchy using PostgreSQL dialect"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java index 44b96d4a40..600206c148 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -16,24 +16,16 @@ package com.example.spanner; -import com.google.api.gax.longrunning.OperationFuture; import com.google.api.gax.paging.Page; import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; -import com.google.cloud.spanner.Dialect; -import com.google.cloud.spanner.Instance; -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.InstanceId; import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; -import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ReadOnlyTransaction; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.Spanner; @@ -45,19 +37,27 @@ 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.CreateDatabaseMetadata; +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.database.v1.UpdateDatabaseDdlMetadata; +import com.google.spanner.admin.instance.v1.InstanceName; import com.google.spanner.v1.ExecuteSqlRequest; import java.math.BigDecimal; -import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -66,6 +66,7 @@ * Example code for using the Cloud Spanner PostgreSQL interface. */ public class PgSpannerSample { + // [START spanner_postgresql_insert_data] static final List SINGERS = Arrays.asList( @@ -83,7 +84,9 @@ public class PgSpannerSample { new Album(2, 3, "Terrified")); // [END spanner_postgresql_insert_data] - /** Class to contain performance sample data. */ + /** + * Class to contain performance sample data. + */ static class Performance { final long singerId; @@ -159,7 +162,9 @@ static class Performance { new BigDecimal("390650.99"))); // [END spanner_postgresql_insert_datatypes_data] - /** Class to contain venue sample data. */ + /** + * Class to contain venue sample data. + */ static class Venue { final long venueId; @@ -195,15 +200,18 @@ static class Venue { } // [START spanner_postgresql_create_database] - static void createPostgreSqlDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { - OperationFuture op = dbAdminClient.createDatabase( - dbAdminClient.newDatabaseBuilder(id).setDialect(Dialect.POSTGRESQL).build(), - Collections.emptyList()); + 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 = op.get(); - System.out.println("Created database [" + db.getId() + "]"); - createTableUsingDdl(dbAdminClient, id); + 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(); @@ -272,9 +280,9 @@ static void deleteExampleData(DatabaseClient dbClient) { // [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"))) { + 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), @@ -287,12 +295,12 @@ static void query(DatabaseClient dbClient) { // [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"))) { + 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), @@ -303,15 +311,12 @@ static void read(DatabaseClient dbClient) { // [END spanner_postgresql_read_data] // [START spanner_postgresql_add_column] - static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) { - OperationFuture op = adminClient.updateDatabaseDdl( - dbId.getInstanceId().getInstance(), - dbId.getDatabase(), - Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget bigint"), - null); + static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); + 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. @@ -404,11 +409,11 @@ static void queryMarketingBudget(DatabaseClient dbClient) { // 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"))) { + 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", @@ -424,16 +429,12 @@ static void queryMarketingBudget(DatabaseClient dbClient) { // [END spanner_postgresql_query_data_with_new_column] // [START spanner_postgresql_create_index] - static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { - OperationFuture op = - adminClient.updateDatabaseDdl( - dbId.getInstanceId().getInstance(), - dbId.getDatabase(), - Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), - null); + static void addIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); + 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. @@ -449,13 +450,13 @@ static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { // [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"))) { + 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)); } @@ -464,17 +465,14 @@ static void readUsingIndex(DatabaseClient dbClient) { // [END spanner_postgresql_read_data_with_index] // [START spanner_postgresql_create_storing_index] - static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { - OperationFuture op = adminClient.updateDatabaseDdl( - dbId.getInstanceId().getInstance(), - dbId.getDatabase(), - Arrays.asList( - "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) " - + "INCLUDE (MarketingBudget)"), - null); + static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); + 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. @@ -493,13 +491,13 @@ static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { 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"))) { + dbClient + .singleUse() + .readUsingIndex( + "Albums", + "AlbumsByAlbumTitle2", + KeySet.all(), + Arrays.asList("AlbumId", "AlbumTitle", "MarketingBudget"))) { while (resultSet.next()) { System.out.printf( "%d %s %s\n", @@ -526,8 +524,8 @@ static void readOnlyTransaction(DatabaseClient dbClient) { queryResultSet.getString(2)); } try (ResultSet readResultSet = - transaction.read( - "Albums", KeySet.all(), Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) { + transaction.read( + "Albums", KeySet.all(), Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) { while (readResultSet.next()) { System.out.printf( "%d %d %s\n", @@ -542,10 +540,10 @@ static void readOnlyTransaction(DatabaseClient dbClient) { // [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"))) { + 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", @@ -656,35 +654,31 @@ static void writeWithTransactionUsingDml(DatabaseClient dbClient) { // [START spanner_postgresql_create_table_using_ddl] // [START spanner_postgresql_create_database] - static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseId id) { - OperationFuture op = - dbAdminClient.updateDatabaseDdl( - id.getInstanceId().getInstance(), - id.getDatabase(), - Arrays.asList( - "CREATE TABLE Singers (" - + " SingerId bigint NOT NULL," - + " FirstName character varying(1024)," - + " LastName character varying(1024)," - + " SingerInfo bytea," - + " 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"), - null); + static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); - System.out.println("Created Singers & Albums tables in database: [" + id + "]"); + 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 (SpannerException) e.getCause(); + 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. @@ -697,11 +691,11 @@ static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseId id // [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"))) { + 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", @@ -749,17 +743,14 @@ static void updateWithTimestamp(DatabaseClient dbClient) { // [END spanner_postgresql_update_data_with_timestamp_column] // [START spanner_postgresql_add_timestamp_column] - static void addLastUpdateTimestampColumn(DatabaseAdminClient adminClient, DatabaseId dbId) { - OperationFuture op = - adminClient.updateDatabaseDdl( - dbId.getInstanceId().getInstance(), - dbId.getDatabase(), - Arrays.asList( - "ALTER TABLE Albums ADD COLUMN LastUpdateTime spanner.commit_timestamp"), - null); + static void addLastUpdateTimestampColumn( + DatabaseAdminClient adminClient, DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); + 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. @@ -778,14 +769,14 @@ static void queryMarketingBudgetWithTimestamp(DatabaseClient dbClient) { // 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"))) { + 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", @@ -801,24 +792,20 @@ static void queryMarketingBudgetWithTimestamp(DatabaseClient dbClient) { // [END spanner_postgresql_query_data_with_timestamp_column] // [START spanner_postgresql_create_table_with_timestamp_column] - static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, DatabaseId id) { - OperationFuture op = - dbAdminClient.updateDatabaseDdl( - id.getInstanceId().getInstance(), - id.getDatabase(), - Arrays.asList( - "CREATE TABLE Performances (" - + " SingerId BIGINT NOT NULL," - + " VenueId BIGINT NOT NULL," - + " Revenue BIGINT," - + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," - + " PRIMARY KEY (SingerId, VenueId))" - + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), - null); + static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, + DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); - System.out.println("Created Performances table in database: [" + id + "]"); + 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(); @@ -855,13 +842,13 @@ static void queryPerformancesTable(DatabaseClient dbClient) { // 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"))) { + 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", @@ -994,27 +981,24 @@ static void updateUsingBatchDml(DatabaseClient dbClient) { // [END spanner_postgresql_dml_batch_update] // [START spanner_postgresql_create_table_with_datatypes] - static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, DatabaseId id) { - OperationFuture op = - dbAdminClient.updateDatabaseDdl( - id.getInstanceId().getInstance(), - id.getDatabase(), - Arrays.asList( - "CREATE TABLE Venues (" - + " VenueId BIGINT NOT NULL," - + " VenueName character varying(100)," - + " VenueInfo bytea," - + " Capacity BIGINT," - + " OutdoorVenue BOOL, " - + " PopularityScore FLOAT8, " - + " Revenue NUMERIC, " - + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," - + " PRIMARY KEY (VenueId))"), - null); + static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, + DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); - System.out.println("Created Venues table in database: [" + id + "]"); + 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(); @@ -1222,9 +1206,9 @@ static void clientWithQueryOptions(DatabaseId db) { Spanner spanner = options.getService(); DatabaseClient dbClient = spanner.getDatabaseClient(db); try (ResultSet resultSet = - dbClient - .singleUse() - .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) { + 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)); @@ -1236,19 +1220,19 @@ static void clientWithQueryOptions(DatabaseId db) { // [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())) { + 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)); @@ -1258,22 +1242,26 @@ static void queryWithQueryOptions(DatabaseClient dbClient) { // [END spanner_postgresql_query_with_query_options] // [START spanner_postgresql_list_backup_operations] - static void listBackupOperations(InstanceAdminClient instanceAdminClient, DatabaseId databaseId) { - Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); - // Get create backup operations for the sample database. - Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( - TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, - TimeUnit.HOURS), 0); + 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.database:%s) AND " - + "(metadata.@type:type.googleapis.com/" - + "google.spanner.admin.database.v1.CreateBackupMetadata) AND " - + "(metadata.progress.start_time > \"%s\")", - databaseId.getName(), last24Hours); - Page operations = instance - .listBackupOperations(Options.filter(filter)); - for (com.google.longrunning.Operation op : operations.iterateAll()) { + "(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( @@ -1287,23 +1275,55 @@ static void listBackupOperations(InstanceAdminClient instanceAdminClient, Databa System.err.println(e.getMessage()); } } + // Get copy backup operations for the sample database. + filter = String.format( + "(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.CopyBackupMetadata) " + + "AND (metadata.source_backup:%s)", + 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( - InstanceAdminClient instanceAdminClient, - DatabaseAdminClient dbAdminClient, - InstanceId instanceId) { - Instance instance = instanceAdminClient.getInstance(instanceId.getInstance()); + DatabaseAdminClient dbAdminClient, String projectId, String instanceId) { // Get optimize restored database operations. - Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( - TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, - TimeUnit.HOURS), 0); + 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); - for (Operation op : instance.listDatabaseOperations(Options.filter(filter)).iterateAll()) { + 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); @@ -1322,12 +1342,15 @@ static void listDatabaseOperations( static void run( DatabaseClient dbClient, DatabaseAdminClient dbAdminClient, - InstanceAdminClient instanceAdminClient, String command, - DatabaseId database) { + DatabaseId database, + String backupId) { + DatabaseName databaseName = DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase()); switch (command) { - case "createdatabase": - createPostgreSqlDatabase(dbAdminClient, database); + case "createpgdatabase": + createPostgreSqlDatabase(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase()); break; case "write": writeExampleData(dbClient); @@ -1342,7 +1365,7 @@ static void run( read(dbClient); break; case "addmarketingbudget": - addMarketingBudget(dbAdminClient, database); + addMarketingBudget(dbAdminClient, databaseName); break; case "update": update(dbClient); @@ -1354,13 +1377,13 @@ static void run( queryMarketingBudget(dbClient); break; case "addindex": - addIndex(dbAdminClient, database); + addIndex(dbAdminClient, databaseName); break; case "readindex": readUsingIndex(dbClient); break; case "addstoringindex": - addStoringIndex(dbAdminClient, database); + addStoringIndex(dbAdminClient, databaseName); break; case "readstoringindex": readStoringIndex(dbClient); @@ -1381,13 +1404,13 @@ static void run( writeWithTransactionUsingDml(dbClient); break; case "createtableusingddl": - createTableUsingDdl(dbAdminClient, database); + createTableUsingDdl(dbAdminClient, databaseName); break; case "readstaledata": readStaleData(dbClient); break; case "addlastupdatetimestampcolumn": - addLastUpdateTimestampColumn(dbAdminClient, database); + addLastUpdateTimestampColumn(dbAdminClient, databaseName); break; case "updatewithtimestamp": updateWithTimestamp(dbClient); @@ -1396,7 +1419,7 @@ static void run( queryMarketingBudgetWithTimestamp(dbClient); break; case "createtablewithtimestamp": - createTableWithTimestamp(dbAdminClient, database); + createTableWithTimestamp(dbAdminClient, databaseName); break; case "writewithtimestamp": writeExampleDataWithTimestamp(dbClient); @@ -1426,7 +1449,7 @@ static void run( updateUsingBatchDml(dbClient); break; case "createtablewithdatatypes": - createTableWithDatatypes(dbAdminClient, database); + createTableWithDatatypes(dbAdminClient, databaseName); break; case "writedatatypesdata": writeDatatypesData(dbClient); @@ -1459,10 +1482,12 @@ static void run( queryWithQueryOptions(dbClient); break; case "listbackupoperations": - listBackupOperations(instanceAdminClient, database); + listBackupOperations(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase(), backupId); break; case "listdatabaseoperations": - listDatabaseOperations(instanceAdminClient, dbAdminClient, database.getInstanceId()); + listDatabaseOperations(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance()); break; default: printUsageAndExit(); @@ -1525,9 +1550,10 @@ public static void main(String[] args) { // [START spanner_init_client] SpannerOptions options = SpannerOptions.newBuilder().build(); Spanner spanner = options.getService(); + DatabaseAdminClient dbAdminClient = null; try { // [END spanner_init_client] - String command = args[0]; + 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. @@ -1539,23 +1565,35 @@ public static void main(String[] args) { + 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); - DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + dbAdminClient = spanner.createDatabaseAdminClient(); // [END spanner_init_client] // Use client here... - run(dbClient, dbAdminClient, instanceAdminClient, command, db); + 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. */ + /** + * Class to contain singer sample data. + */ static class Singer { final long singerId; @@ -1569,7 +1607,9 @@ static class Singer { } } - /** Class to contain album sample data. */ + /** + * Class to contain album sample data. + */ static class Album { final long singerId; diff --git a/samples/snippets/src/main/java/com/example/spanner/RestoreBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/RestoreBackupWithEncryptionKey.java index fa87d98151..af101f96cc 100644 --- a/samples/snippets/src/main/java/com/example/spanner/RestoreBackupWithEncryptionKey.java +++ b/samples/snippets/src/main/java/com/example/spanner/RestoreBackupWithEncryptionKey.java @@ -18,17 +18,17 @@ // [START spanner_restore_backup_with_encryption_key] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.BackupId; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; -import com.google.cloud.spanner.DatabaseId; -import com.google.cloud.spanner.Restore; +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.encryption.EncryptionConfigs; -import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata; +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.InstanceName; +import com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig; +import com.google.spanner.admin.database.v1.RestoreDatabaseRequest; import java.util.concurrent.ExecutionException; public class RestoreBackupWithEncryptionKey { @@ -43,8 +43,8 @@ static void restoreBackupWithEncryptionKey() { "projects/" + projectId + "/locations//keyRings//cryptoKeys/"; try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) { restoreBackupWithEncryptionKey( adminClient, projectId, @@ -57,19 +57,18 @@ static void restoreBackupWithEncryptionKey() { static Void restoreBackupWithEncryptionKey(DatabaseAdminClient adminClient, String projectId, String instanceId, String backupId, String restoreId, String kmsKeyName) { - final Restore restore = adminClient - .newRestoreBuilder( - BackupId.of(projectId, instanceId, backupId), - DatabaseId.of(projectId, instanceId, restoreId)) - .setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName)) - .build(); - final OperationFuture operation = adminClient - .restoreDatabase(restore); - + 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 = operation.get(); + database = adminClient.restoreDatabaseAsync(request).get(); + ; } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw SpannerExceptionFactory.asSpannerException(e.getCause()); @@ -81,9 +80,9 @@ static Void restoreBackupWithEncryptionKey(DatabaseAdminClient adminClient, System.out.printf( "Database %s restored to %s from backup %s using encryption key %s%n", - database.getRestoreInfo().getSourceDatabase(), - database.getId(), - database.getRestoreInfo().getBackup(), + database.getRestoreInfo().getBackupInfo().getSourceDatabase(), + database.getName(), + database.getRestoreInfo().getBackupInfo().getBackup(), database.getEncryptionConfig().getKmsKeyName() ); return null; diff --git a/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java index 43c04aaa8d..d406225c28 100644 --- a/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/SpannerSample.java @@ -20,28 +20,19 @@ import com.google.api.gax.longrunning.OperationFuture; import com.google.api.gax.longrunning.OperationSnapshot; -import com.google.api.gax.paging.Page; 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.Timestamp; -import com.google.cloud.spanner.Backup; -import com.google.cloud.spanner.BackupId; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; -import com.google.cloud.spanner.Instance; -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.InstanceId; import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; -import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ReadOnlyTransaction; -import com.google.cloud.spanner.RestoreInfo; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerBatchUpdateException; @@ -53,27 +44,41 @@ 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.CreateDatabaseMetadata; +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.UpdateDatabaseDdlMetadata; +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; -import org.threeten.bp.LocalDate; -import org.threeten.bp.LocalDateTime; -import org.threeten.bp.OffsetDateTime; -import org.threeten.bp.temporal.ChronoField; /** * Example code for using the Cloud Spanner API. This example demonstrates all the common operations @@ -93,7 +98,9 @@ */ public class SpannerSample { - /** Class to contain singer sample data. */ + /** + * Class to contain singer sample data. + */ static class Singer { final long singerId; @@ -107,7 +114,9 @@ static class Singer { } } - /** Class to contain album sample data. */ + /** + * Class to contain album sample data. + */ static class Album { final long singerId; @@ -121,7 +130,9 @@ static class Album { } } - /** Class to contain performance sample data. */ + /** + * Class to contain performance sample data. + */ static class Performance { final long singerId; @@ -137,7 +148,9 @@ static class Performance { } } - /** Class to contain venue sample data. */ + /** + * Class to contain venue sample data. + */ static class Venue { final long venueId; @@ -175,17 +188,6 @@ static class Venue { } } - /** Get a database id to restore a backup to from the sample database id. */ - static String createRestoredSampleDbId(DatabaseId database) { - int index = database.getDatabase().indexOf('-'); - String prefix = database.getDatabase().substring(0, index); - String restoredDbId = database.getDatabase().replace(prefix, "restored"); - if (restoredDbId.length() > 30) { - restoredDbId = restoredDbId.substring(0, 30); - } - return restoredDbId; - } - // [START spanner_insert_data] static final List SINGERS = Arrays.asList( @@ -272,12 +274,13 @@ static String createRestoredSampleDbId(DatabaseId database) { // [END spanner_insert_datatypes_data] // [START spanner_create_database] - static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { - OperationFuture op = - dbAdminClient.createDatabase( - id.getInstanceId().getInstance(), - id.getDatabase(), - Arrays.asList( + 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)," @@ -291,11 +294,12 @@ static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { + " AlbumId INT64 NOT NULL," + " AlbumTitle STRING(MAX)" + ") PRIMARY KEY (SingerId, AlbumId)," - + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")); + + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")).build(); try { // Initiate the request which returns an OperationFuture. - Database db = op.get(); - System.out.println("Created database [" + db.getId() + "]"); + 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(); @@ -308,25 +312,23 @@ static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { // [END spanner_create_database] // [START spanner_create_table_with_timestamp_column] - static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, DatabaseId id) { - OperationFuture op = - dbAdminClient.updateDatabaseDdl( - id.getInstanceId().getInstance(), - id.getDatabase(), - Arrays.asList( - "CREATE TABLE Performances (" - + " SingerId 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"), - null); + static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, + DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); - System.out.println("Created Performances table in database: [" + id + "]"); + 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(); @@ -446,16 +448,12 @@ static void read(DatabaseClient dbClient) { // [END spanner_read_data] // [START spanner_add_column] - static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) { - OperationFuture op = - adminClient.updateDatabaseDdl( - dbId.getInstanceId().getInstance(), - dbId.getDatabase(), - Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget INT64"), - null); + static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); + 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. @@ -565,16 +563,12 @@ static void queryMarketingBudget(DatabaseClient dbClient) { // [END spanner_query_data_with_new_column] // [START spanner_create_index] - static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { - OperationFuture op = - adminClient.updateDatabaseDdl( - dbId.getInstanceId().getInstance(), - dbId.getDatabase(), - Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), - null); + static void addIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); + 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. @@ -636,18 +630,14 @@ static void readUsingIndex(DatabaseClient dbClient) { // [END spanner_read_data_with_index] // [START spanner_create_storing_index] - static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { - OperationFuture op = - adminClient.updateDatabaseDdl( - dbId.getInstanceId().getInstance(), - dbId.getDatabase(), - Arrays.asList( - "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) " - + "STORING (MarketingBudget)"), - null); + static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); + 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. @@ -729,18 +719,14 @@ static void readStaleData(DatabaseClient dbClient) { // [END spanner_read_stale_data] // [START spanner_add_timestamp_column] - static void addCommitTimestamp(DatabaseAdminClient adminClient, DatabaseId dbId) { - OperationFuture op = - adminClient.updateDatabaseDdl( - dbId.getInstanceId().getInstance(), - dbId.getDatabase(), - Arrays.asList( - "ALTER TABLE Albums ADD COLUMN LastUpdateTime TIMESTAMP " - + "OPTIONS (allow_commit_timestamp=true)"), - null); + static void addCommitTimestamp(DatabaseAdminClient adminClient, DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); + 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. @@ -1186,18 +1172,18 @@ static void writeWithTransactionUsingDml(DatabaseClient dbClient) { album2Budget -= transfer; Statement updateStatement = Statement.newBuilder( - "UPDATE Albums " - + "SET MarketingBudget = @AlbumBudget " - + "WHERE SingerId = 1 and AlbumId = 1") + "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") + "UPDATE Albums " + + "SET MarketingBudget = @AlbumBudget " + + "WHERE SingerId = 2 and AlbumId = 2") .bind("AlbumBudget") .to(album2Budget) .build(); @@ -1255,30 +1241,26 @@ static void updateUsingBatchDml(DatabaseClient dbClient) { // [END spanner_dml_batch_update] // [START spanner_create_table_with_datatypes] - static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, DatabaseId id) { - OperationFuture op = - dbAdminClient.updateDatabaseDdl( - id.getInstanceId().getInstance(), - id.getDatabase(), - Arrays.asList( - "CREATE TABLE Venues (" - + " VenueId 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)"), - null); + static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, + DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. - op.get(); - System.out.println("Created Venues table in database: [" + id + "]"); + 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(); @@ -1570,74 +1552,76 @@ static void queryWithQueryOptions(DatabaseClient dbClient) { // [END spanner_query_with_query_options] // [START spanner_create_backup] - static void createBackup(DatabaseAdminClient dbAdminClient, DatabaseId databaseId, - BackupId backupId, Timestamp versionTime) { + 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.ofTimeMicroseconds(TimeUnit.MICROSECONDS.convert( - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS)); - Backup backup = - dbAdminClient - .newBackupBuilder(backupId) - .setDatabase(databaseId) - .setExpireTime(expireTime) - .setVersionTime(versionTime) - .build(); + 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 [" + backup.getId() + "]..."); - OperationFuture op = backup.create(); + System.out.println("Creating backup [" + backupId + "]..."); try { // Wait for the backup operation to complete. - backup = op.get(); - System.out.println("Created backup [" + backup.getId() + "]"); + backup = dbAdminClient.createBackupAsync( + InstanceName.of(projectId, instanceId), backup, backupId).get(); + System.out.println("Created backup [" + backup.getName() + "]"); } catch (ExecutionException e) { - throw (SpannerException) e.getCause(); + throw SpannerExceptionFactory.asSpannerException(e); } catch (InterruptedException e) { throw SpannerExceptionFactory.propagateInterrupt(e); } // Reload the metadata of the backup from the server. - backup = backup.reload(); + 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.getId().getName(), - backup.getSize(), - LocalDateTime.ofEpochSecond( - backup.getProto().getCreateTime().getSeconds(), - backup.getProto().getCreateTime().getNanos(), - OffsetDateTime.now().getOffset()), - LocalDateTime.ofEpochSecond( - backup.getProto().getVersionTime().getSeconds(), - backup.getProto().getVersionTime().getNanos(), - OffsetDateTime.now().getOffset()) - )); + 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, DatabaseId databaseId, BackupId backupId) { + DatabaseAdminClient dbAdminClient, String projectId, String instanceId, + String databaseId, String backupId) { // Set expire time to 14 days from now. - Timestamp expireTime = Timestamp.ofTimeMicroseconds(TimeUnit.MICROSECONDS.convert( - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS)); - - // Create a backup instance. - Backup backup = - dbAdminClient - .newBackupBuilder(backupId) - .setDatabase(databaseId) - .setExpireTime(expireTime) - .build(); - // Start the creation of a backup. - System.out.println("Creating backup [" + backup.getId() + "]..."); - OperationFuture op = backup.create(); + 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 [" + backup.getId() + "]..."); - dbAdminClient.cancelOperation(op.getName()); + 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. @@ -1647,11 +1631,11 @@ static void cancelCreateBackup( } if (pollingFuture.get().getErrorCode() == null) { // Backup was created before it could be cancelled. Delete the backup. - backup.delete(); - System.out.println("Backup operation for [" + backup.getId() + 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 [" + backup.getId() + "] successfully cancelled"); + System.out.println("Backup operation for [" + backupId + "] successfully cancelled"); } } catch (ExecutionException e) { throw SpannerExceptionFactory.newSpannerException(e.getCause()); @@ -1663,20 +1647,22 @@ static void cancelCreateBackup( // [START spanner_list_backup_operations] static void listBackupOperations( - InstanceAdminClient instanceAdminClient, DatabaseId databaseId, BackupId backupId) { - Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); - // Get create backup operations for the sample database. - Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( - TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, - TimeUnit.HOURS), 0); + 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)", - databaseId.getName()); - Page createBackupOperations = instance.listBackupOperations( - Options.filter(filter)); + "(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 { @@ -1693,24 +1679,27 @@ static void listBackupOperations( } } // Get copy backup operations for the sample database. - filter = - String.format( - "(metadata.@type:type.googleapis.com/" - + "google.spanner.admin.database.v1.CopyBackupMetadata) " - + "AND (metadata.source_backup:%s)", - backupId.getName()); - Page copyBackupOperations = instance.listBackupOperations(Options.filter(filter)); + 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); + 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())); + 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()); @@ -1721,18 +1710,23 @@ static void listBackupOperations( // [START spanner_list_database_operations] static void listDatabaseOperations( - InstanceAdminClient instanceAdminClient, - DatabaseAdminClient dbAdminClient, - InstanceId instanceId) { - Instance instance = instanceAdminClient.getInstance(instanceId.getInstance()); + DatabaseAdminClient dbAdminClient, String projectId, String instanceId) { // Get optimize restored database operations. - Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( - TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, - TimeUnit.HOURS), 0); + 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); - for (Operation op : instance.listDatabaseOperations(Options.filter(filter)).iterateAll()) { + + "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); @@ -1750,74 +1744,92 @@ static void listDatabaseOperations( // [START spanner_list_backups] static void listBackups( - InstanceAdminClient instanceAdminClient, DatabaseId databaseId, BackupId backupId) { - Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); + 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 : instance.listBackups().iterateAll()) { + 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.getBackup())); - for (Backup backup : instance.listBackups( - Options.filter(String.format("name:%s", backupId.getBackup()))).iterateAll()) { + 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.getDatabase())); - for (Backup backup : instance.listBackups( - Options.filter(String.format("database:%s", databaseId.getDatabase()))).iterateAll()) { + "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. - Timestamp expireTime = 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.toString())); - for (Backup backup : - instance.listBackups( - Options.filter(String.format("expire_time < \"%s\"", expireTime.toString()))) - .iterateAll()) { + 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 : instance.listBackups(Options.filter("size_bytes > 100")).iterateAll()) { + 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. - Timestamp createTime = Timestamp.ofTimeMicroseconds(TimeUnit.MICROSECONDS.convert( - System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1), TimeUnit.MILLISECONDS)); + 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())); - for (Backup backup : - instance - .listBackups(Options.filter( - String.format("create_time >= \"%s\" AND state:READY", createTime.toString()))) - .iterateAll()) { + 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:"); - Page page = instance.listBackups(Options.pageSize(10)); + listBackupsRequest = + ListBackupsRequest.newBuilder().setParent(instanceName.toString()).setPageSize(10).build(); while (true) { - for (Backup backup : page.getValues()) { + ListBackupsPagedResponse response = dbAdminClient.listBackups(listBackupsRequest); + for (Backup backup : response.getPage().iterateAll()) { System.out.println(backup); } - if (!page.hasNextPage()) { + String nextPageToken = response.getNextPageToken(); + if (!Strings.isNullOrEmpty(nextPageToken)) { + listBackupsRequest = listBackupsRequest.toBuilder().setPageToken(nextPageToken).build(); + } else { break; } - page = page.getNextPage(); } } // [END spanner_list_backups] @@ -1825,31 +1837,35 @@ static void listBackups( // [START spanner_restore_backup] static void restoreBackup( DatabaseAdminClient dbAdminClient, - BackupId backupId, - DatabaseId sourceDatabaseId, - DatabaseId restoreToDatabase) { - Backup backup = dbAdminClient.newBackupBuilder(backupId).build(); + 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.getId().toString(), - restoreToDatabase.toString())); + "Restoring backup [%s] to database [%s]...", backup.getName(), restoreToDatabaseId)); try { - OperationFuture op = backup.restore(restoreToDatabase); + 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. - Database db = op.get(); - // Refresh database metadata and get the restore info. - RestoreInfo restore = db.reload().getRestoreInfo(); - Timestamp versionTime = Timestamp.fromProto(restore - .getProto() - .getBackupInfo() - .getVersionTime()); + 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 [" - + restore.getSourceDatabase().getName() + + db.getName() + "] from [" - + restore.getBackup().getName() - + "] with version time [" + versionTime + "]"); + + restoreInfo.getBackupInfo().getBackup() + + "] with version time [" + backupInfo.getVersionTime() + "]"); } catch (ExecutionException e) { throw SpannerExceptionFactory.newSpannerException(e.getCause()); } catch (InterruptedException e) { @@ -1859,49 +1875,59 @@ static void restoreBackup( // [END spanner_restore_backup] // [START spanner_update_backup] - static void updateBackup(DatabaseAdminClient dbAdminClient, BackupId backupId) { + 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.newBackupBuilder(backupId).build().reload(); + 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 expireTime = - Timestamp.ofTimeMicroseconds( - TimeUnit.SECONDS.toMicros(backup.getExpireTime().getSeconds()) - + TimeUnit.NANOSECONDS.toMicros(backup.getExpireTime().getNanos()) + 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 - expireTime = expireTime.compareTo(backup.getMaxExpireTime()) - < 0 ? expireTime : backup.getMaxExpireTime(); - int timeDiff = expireTime.compareTo(backup.getExpireTime()); - Timestamp newExpireTime = (timeDiff < 0) ? expireTime : backup.getExpireTime(); + 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(), - LocalDateTime.ofEpochSecond( - expireTime.getSeconds(), - expireTime.getNanos(), - OffsetDateTime.now().getOffset()).toString())); + java.time.OffsetDateTime.ofInstant( + Instant.ofEpochSecond(newExpireTime.getSeconds(), + newExpireTime.getNanos()), ZoneId.systemDefault()))); // Update expire time. - backup = backup.toBuilder().setExpireTime(expireTime).build(); - backup.updateExpireTime(); + 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, BackupId backupId) { - Backup backup = dbAdminClient.newBackupBuilder(backupId).build(); + 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 + "]..."); - backup.delete(); + dbAdminClient.deleteBackup(backupName); // Verify that the backup is deleted. - if (backup.exists()) { - System.out.println("Delete backup [" + backupId + "] failed"); - throw new RuntimeException("Delete backup [" + backupId + "] failed"); - } else { - System.out.println("Deleted backup [" + backupId + "]"); + 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] @@ -1909,13 +1935,13 @@ static void deleteBackup(DatabaseAdminClient dbAdminClient, BackupId backupId) { static void run( DatabaseClient dbClient, DatabaseAdminClient dbAdminClient, - InstanceAdminClient instanceAdminClient, String command, DatabaseId database, - BackupId backup) { + String backupId) { switch (command) { case "createdatabase": - createDatabase(dbAdminClient, database); + createDatabase(dbAdminClient, InstanceName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance()), database.getDatabase()); break; case "write": writeExampleData(dbClient); @@ -1930,7 +1956,8 @@ static void run( read(dbClient); break; case "addmarketingbudget": - addMarketingBudget(dbAdminClient, database); + addMarketingBudget(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase())); break; case "update": update(dbClient); @@ -1942,7 +1969,8 @@ static void run( queryMarketingBudget(dbClient); break; case "addindex": - addIndex(dbAdminClient, database); + addIndex(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase())); break; case "readindex": readUsingIndex(dbClient); @@ -1951,7 +1979,8 @@ static void run( queryUsingIndex(dbClient); break; case "addstoringindex": - addStoringIndex(dbAdminClient, database); + addStoringIndex(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase())); break; case "readstoringindex": readStoringIndex(dbClient); @@ -1963,7 +1992,8 @@ static void run( readStaleData(dbClient); break; case "addcommittimestamp": - addCommitTimestamp(dbAdminClient, database); + addCommitTimestamp(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase())); break; case "updatewithtimestamp": updateWithTimestamp(dbClient); @@ -1972,7 +2002,9 @@ static void run( queryMarketingBudgetWithTimestamp(dbClient); break; case "createtablewithtimestamp": - createTableWithTimestamp(dbAdminClient, database); + createTableWithTimestamp(dbAdminClient, + DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase())); break; case "writewithtimestamp": writeExampleDataWithTimestamp(dbClient); @@ -2035,7 +2067,9 @@ static void run( updateUsingBatchDml(dbClient); break; case "createtablewithdatatypes": - createTableWithDatatypes(dbAdminClient, database); + createTableWithDatatypes(dbAdminClient, + DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase())); break; case "writedatatypesdata": writeDatatypesData(dbClient); @@ -2074,35 +2108,41 @@ static void run( queryWithQueryOptions(dbClient); break; case "createbackup": - createBackup(dbAdminClient, database, backup, getVersionTime(dbClient)); + createBackup(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase(), + backupId, getVersionTime(dbClient)); break; case "cancelcreatebackup": cancelCreateBackup( dbAdminClient, - database, - BackupId.of(backup.getInstanceId(), backup.getBackup() + "_cancel")); + database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase(), + backupId + "_cancel"); break; case "listbackupoperations": - listBackupOperations(instanceAdminClient, database, backup); + listBackupOperations(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase(), backupId); break; case "listdatabaseoperations": - listDatabaseOperations(instanceAdminClient, dbAdminClient, database.getInstanceId()); + listDatabaseOperations(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance()); break; case "listbackups": - listBackups(instanceAdminClient, database, backup); + listBackups(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase(), backupId); break; case "restorebackup": restoreBackup( - dbAdminClient, - backup, - database, - DatabaseId.of(database.getInstanceId(), createRestoredSampleDbId(database))); + dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), backupId, database.getDatabase()); break; case "updatebackup": - updateBackup(dbAdminClient, backup); + updateBackup(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), backupId); break; case "deletebackup": - deleteBackup(dbAdminClient, backup); + deleteBackup(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), backupId); break; default: printUsageAndExit(); @@ -2111,13 +2151,13 @@ static void run( static Timestamp getVersionTime(DatabaseClient dbClient) { // Generates a version time for the backup - Timestamp versionTime; + com.google.cloud.Timestamp versionTime; try (ResultSet resultSet = dbClient.singleUse() .executeQuery(Statement.of("SELECT CURRENT_TIMESTAMP()"))) { resultSet.next(); versionTime = resultSet.getTimestamp(0); } - return versionTime; + return versionTime.toProto(); } static void printUsageAndExit() { @@ -2185,15 +2225,16 @@ 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(); } // [START init_client] SpannerOptions options = SpannerOptions.newBuilder().build(); Spanner spanner = options.getService(); + DatabaseAdminClient dbAdminClient = null; try { - String command = args[0]; + 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. @@ -2206,25 +2247,26 @@ public static void main(String[] args) throws Exception { printUsageAndExit(); } // Generate a backup id for the sample database. - String backupName = - String.format( - "%s_%02d", - db.getDatabase(), LocalDate.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR)); - BackupId backup = BackupId.of(db.getInstanceId(), backupName); + String backupId = null; if (args.length == 4) { - backupName = args[3]; + backupId = args[3]; } // [START init_client] DatabaseClient dbClient = spanner.getDatabaseClient(db); - DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + dbAdminClient = spanner.createDatabaseAdminClient(); + // Use client here... // [END init_client] - run(dbClient, dbAdminClient, instanceAdminClient, command, db, backup); + 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] diff --git a/samples/snippets/src/main/java/com/example/spanner/UpdateDatabaseSample.java b/samples/snippets/src/main/java/com/example/spanner/UpdateDatabaseSample.java index c87c27690a..57bb8e9159 100644 --- a/samples/snippets/src/main/java/com/example/spanner/UpdateDatabaseSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/UpdateDatabaseSample.java @@ -17,15 +17,18 @@ package com.example.spanner; // [START spanner_update_database] + import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; -import com.google.cloud.spanner.DatabaseId; -import com.google.cloud.spanner.DatabaseInfo.DatabaseField; 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; +import com.google.spanner.admin.database.v1.Database; +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.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -37,22 +40,31 @@ static void updateDatabase() { final String projectId = "my-project"; final String instanceId = "my-instance"; final String databaseId = "my-database"; + updateDatabase(projectId, instanceId, databaseId); } - static void updateDatabase(String projectId, String instanceId, String databaseId) { + static void updateDatabase( + String projectId, String instanceId, String databaseId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); - - DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId); - Database databaseToUpdate = - databaseAdminClient.newDatabaseBuilder(dbId).enableDropProtection().build(); + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + final Database database = + Database.newBuilder() + .setName(DatabaseName.of(projectId, instanceId, databaseId).toString()) + .setEnableDropProtection(true).build(); + final UpdateDatabaseRequest updateDatabaseRequest = + UpdateDatabaseRequest.newBuilder() + .setDatabase(database) + .setUpdateMask( + FieldMask.newBuilder().addAllPaths( + Lists.newArrayList("enable_drop_protection")).build()) + .build(); OperationFuture operation = - databaseAdminClient.updateDatabase(databaseToUpdate, DatabaseField.DROP_PROTECTION); - System.out.printf("Waiting for update operation for %s to complete...\n", dbId); + databaseAdminClient.updateDatabaseAsync(updateDatabaseRequest); + System.out.printf("Waiting for update operation for %s to complete...\n", databaseId); Database updatedDb = operation.get(5, TimeUnit.MINUTES); - System.out.printf("Updated database %s.\n", updatedDb.getId().getName()); + System.out.printf("Updated database %s.\n", updatedDb.getName()); } catch (ExecutionException | TimeoutException 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/UpdateDatabaseWithDefaultLeaderSample.java b/samples/snippets/src/main/java/com/example/spanner/UpdateDatabaseWithDefaultLeaderSample.java index f3ce004a69..701240cb12 100644 --- a/samples/snippets/src/main/java/com/example/spanner/UpdateDatabaseWithDefaultLeaderSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/UpdateDatabaseWithDefaultLeaderSample.java @@ -18,13 +18,12 @@ //[START spanner_update_database_with_default_leader] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; -import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient; +import com.google.spanner.admin.database.v1.DatabaseName; import java.util.Collections; import java.util.concurrent.ExecutionException; @@ -41,26 +40,20 @@ static void updateDatabaseWithDefaultLeader() { static void updateDatabaseWithDefaultLeader( String projectId, String instanceId, String databaseId, String defaultLeader) { - try (Spanner spanner = SpannerOptions - .newBuilder() - .setProjectId(projectId) - .build() - .getService()) { - final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); - final OperationFuture operation = databaseAdminClient - .updateDatabaseDdl( - instanceId, - databaseId, + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + databaseAdminClient + .updateDatabaseDdlAsync( + DatabaseName.of(projectId, instanceId, databaseId), Collections.singletonList( String.format( "ALTER DATABASE `%s` SET OPTIONS (default_leader = '%s')", databaseId, defaultLeader ) - ), - null - ); - operation.get(); + ) + ).get(); System.out.println("Updated default leader to " + defaultLeader); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. diff --git a/samples/snippets/src/main/java/com/example/spanner/UpdateInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/UpdateInstanceConfigSample.java index 6450f265e1..c10175abe1 100644 --- a/samples/snippets/src/main/java/com/example/spanner/UpdateInstanceConfigSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/UpdateInstanceConfigSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Google LLC + * 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. @@ -17,21 +17,21 @@ package com.example.spanner; // [START spanner_update_instance_config] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.InstanceConfig; -import com.google.cloud.spanner.InstanceConfigId; -import com.google.cloud.spanner.InstanceConfigInfo; -import com.google.cloud.spanner.InstanceConfigInfo.InstanceConfigField; + 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.spanner.admin.instance.v1.UpdateInstanceConfigMetadata; +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.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; class UpdateInstanceConfigSample { + static void updateInstanceConfig() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; @@ -41,27 +41,42 @@ static void updateInstanceConfig() { static void updateInstanceConfig(String projectId, String instanceConfigId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); - InstanceConfigInfo instanceConfigInfo = - InstanceConfig.newBuilder(InstanceConfigId.of(projectId, instanceConfigId)) + SpannerOptions.newBuilder() + .setProjectId(projectId) + .build() + .getService(); + InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) { + final InstanceConfigName instanceConfigName = + InstanceConfigName.of(projectId, instanceConfigId); + final InstanceConfig instanceConfig = + InstanceConfig.newBuilder() + .setName(instanceConfigName.toString()) .setDisplayName("updated custom instance config") - .addLabel("updated", "true") - .build(); - final OperationFuture operation = - instanceAdminClient.updateInstanceConfig( - instanceConfigInfo, - ImmutableList.of(InstanceConfigField.DISPLAY_NAME, InstanceConfigField.LABELS)); + .putLabels("updated", "true").build(); + /** + * The field mask must always be specified; this prevents any future + * fields in [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig] + * from being erased accidentally by clients that do not know about them. + */ + final UpdateInstanceConfigRequest updateInstanceConfigRequest = + UpdateInstanceConfigRequest.newBuilder() + .setInstanceConfig(instanceConfig) + .setUpdateMask( + FieldMask.newBuilder().addAllPaths(ImmutableList.of("display_name", "labels")) + .build()).build(); try { - System.out.printf("Waiting for update operation on %s to complete...\n", instanceConfigId); - InstanceConfig instanceConfig = operation.get(5, TimeUnit.MINUTES); + System.out.printf("Waiting for update operation on %s to complete...\n", + instanceConfigName); + InstanceConfig instanceConfigResult = + instanceAdminClient.updateInstanceConfigAsync( + updateInstanceConfigRequest).get(5, TimeUnit.MINUTES); System.out.printf( "Updated instance configuration %s with new display name %s\n", - instanceConfig.getId(), instanceConfig.getDisplayName()); + instanceConfigResult.getName(), instanceConfig.getDisplayName()); } catch (ExecutionException | TimeoutException e) { System.out.printf( "Error: Updating instance config %s failed with error message %s\n", - instanceConfigInfo.getId(), e.getMessage()); + instanceConfig.getName(), e.getMessage()); e.printStackTrace(); } catch (InterruptedException e) { System.out.println( diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/archived/.gitkeep b/samples/snippets/src/main/java/com/example/spanner/admin/archived/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/archived/AddAndDropDatabaseRole.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/AddAndDropDatabaseRole.java new file mode 100644 index 0000000000..cce8543492 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/AddAndDropDatabaseRole.java @@ -0,0 +1,88 @@ +/* + * 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.archived; + +// [START spanner_add_and_drop_database_role] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.common.collect.ImmutableList; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class AddAndDropDatabaseRole { + + static void addAndDropDatabaseRole() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + String parentRole = "my-new-parent-role"; + String childRole = "my-new-child-role"; + addAndDropDatabaseRole(projectId, instanceId, databaseId, parentRole, childRole); + } + + static void addAndDropDatabaseRole( + String projectId, String instanceId, String databaseId, String parentRole, String childRole) { + try (Spanner spanner = + SpannerOptions.newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + OperationFuture operation = + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of( + "CREATE ROLE " + parentRole, + "GRANT SELECT ON TABLE Albums TO ROLE " + parentRole, + "CREATE ROLE " + childRole, + "GRANT ROLE " + parentRole + " TO ROLE " + childRole), + null); + try { + System.out.println("Waiting for role create operation to complete..."); + operation.get(5, TimeUnit.MINUTES); + System.out.printf( + "Created roles %s and %s and granted privileges%n", parentRole, childRole); + // Delete role and membership. + operation = + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of( + "REVOKE ROLE " + parentRole + " FROM ROLE " + childRole, + "DROP ROLE " + childRole), + null); + System.out.println("Waiting for role revoke & drop operation to complete..."); + operation.get(5, TimeUnit.MINUTES); + System.out.printf("Revoked privileges and dropped role %s%n", childRole); + } catch (ExecutionException | TimeoutException e) { + System.out.printf( + "Error: AddAndDropDatabaseRole failed with error message %s\n", e.getMessage()); + e.printStackTrace(); + } catch (InterruptedException e) { + System.out.println( + "Error: Waiting for AddAndDropDatabaseRole operation to finish was interrupted"); + } + } + } +} +// [END spanner_add_and_drop_database_role] 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/archived/AddJsonColumnSample.java similarity index 55% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonColumnSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/AddJsonColumnSample.java index 69d2c229e2..8be7d6bd58 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonColumnSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/AddJsonColumnSample.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_add_json_column] - +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; 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 com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import java.util.concurrent.ExecutionException; class AddJsonColumnSample { @@ -33,24 +33,25 @@ static void addJsonColumn() throws InterruptedException, ExecutionException { String instanceId = "my-instance"; String databaseId = "my-database"; - addJsonColumn(projectId, instanceId, databaseId); + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + addJsonColumn(adminClient, instanceId, databaseId); + } } - static void addJsonColumn(String projectId, String instanceId, String databaseId) + static void addJsonColumn(DatabaseAdminClient adminClient, String instanceId, String databaseId) 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"); - } + OperationFuture operation = + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of("ALTER TABLE Venues ADD COLUMN VenueDetails JSON"), + null); + // Wait for the operation to finish. + // This will throw an ExecutionException if the operation fails. + operation.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/archived/AddJsonbColumnSample.java similarity index 56% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonbColumnSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/AddJsonbColumnSample.java index fa5604e65e..102ea36b0e 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddJsonbColumnSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/AddJsonbColumnSample.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_postgresql_jsonb_add_column] - +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; 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 com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import java.util.concurrent.ExecutionException; class AddJsonbColumnSample { @@ -33,25 +33,25 @@ static void addJsonbColumn() throws InterruptedException, ExecutionException { String instanceId = "my-instance"; String databaseId = "my-database"; - addJsonbColumn(projectId, instanceId, databaseId); + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + addJsonbColumn(adminClient, instanceId, databaseId); + } } - static void addJsonbColumn(String projectId, String instanceId, String databaseId) + static void addJsonbColumn(DatabaseAdminClient adminClient, String instanceId, String databaseId) 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"); - } + OperationFuture operation = + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of("ALTER TABLE Venues ADD COLUMN VenueDetails JSONB"), + null); + // Wait for the operation to finish. + // This will throw an ExecutionException if the operation fails. + operation.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/archived/AddNumericColumnSample.java similarity index 55% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/AddNumericColumnSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/AddNumericColumnSample.java index 09f4e63216..347aaf5a9e 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddNumericColumnSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/AddNumericColumnSample.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_add_numeric_column] - +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; 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 com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import java.util.concurrent.ExecutionException; class AddNumericColumnSample { @@ -33,24 +33,26 @@ static void addNumericColumn() throws InterruptedException, ExecutionException { String instanceId = "my-instance"; String databaseId = "my-database"; - addNumericColumn(projectId, instanceId, databaseId); + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + addNumericColumn(adminClient, instanceId, databaseId); + } } - static void addNumericColumn(String projectId, String instanceId, String databaseId) + static void addNumericColumn( + DatabaseAdminClient adminClient, String instanceId, String databaseId) 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"); - } + OperationFuture operation = + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of("ALTER TABLE Venues ADD COLUMN Revenue NUMERIC"), + null); + // Wait for the operation to finish. + // This will throw an ExecutionException if the operation fails. + operation.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/archived/AlterSequenceSample.java similarity index 89% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterSequenceSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/AlterSequenceSample.java index 08b02b4243..294bfdf6e2 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/AlterSequenceSample.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_alter_sequence] - +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ResultSet; @@ -25,16 +25,13 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; -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.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class AlterSequenceSample { - static void alterSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -45,14 +42,17 @@ static void alterSequence() { static void alterSequence(String projectId, String instanceId, String databaseId) { try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - databaseAdminClient - .updateDatabaseDdlAsync(DatabaseName.of(projectId, instanceId, databaseId), + dbAdminClient + .updateDatabaseDdl( + instanceId, + databaseId, ImmutableList.of( "ALTER SEQUENCE Seq SET OPTIONS " - + "(skip_range_min = 1000, skip_range_max = 5000000)")) + + "(skip_range_min = 1000, skip_range_max = 5000000)"), + null) .get(5, TimeUnit.MINUTES); System.out.println( 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/archived/AlterTableWithForeignKeyDeleteCascadeSample.java similarity index 54% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/AlterTableWithForeignKeyDeleteCascadeSample.java index 60f386c75f..ebf8a3f053 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/AlterTableWithForeignKeyDeleteCascadeSample.java @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_alter_table_with_foreign_key_delete_cascade] - +import com.google.cloud.spanner.DatabaseAdminClient; 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; class AlterTableWithForeignKeyDeleteCascadeSample { @@ -32,28 +30,30 @@ static void alterForeignKeyDeleteCascadeConstraint() { String instanceId = "my-instance"; String databaseId = "my-database"; - alterForeignKeyDeleteCascadeConstraint(projectId, instanceId, databaseId); + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + alterForeignKeyDeleteCascadeConstraint(adminClient, instanceId, databaseId); + } } static void alterForeignKeyDeleteCascadeConstraint( - 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)); - } + DatabaseAdminClient adminClient, String instanceId, String databaseId) { + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of( + "ALTER TABLE ShoppingCarts\n" + + " ADD CONSTRAINT FKShoppingCartsCustomerName\n" + + " FOREIGN KEY (CustomerName)\n" + + " REFERENCES Customers(CustomerName)\n" + + " ON DELETE CASCADE\n"), + null); + 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/archived/CopyBackupSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CopyBackupSample.java new file mode 100644 index 0000000000..3b3c192a5a --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CopyBackupSample.java @@ -0,0 +1,101 @@ +/* + * Copyright 2022 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner.admin.archived; + +// [START spanner_copy_backup] + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Backup; +import com.google.cloud.spanner.BackupId; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.SpannerOptions; +import com.google.spanner.admin.database.v1.CopyBackupMetadata; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +public class CopyBackupSample { + static void copyBackup() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String sourceBackupId = "my-backup"; + String destinationBackupId = "my-destination-backup"; + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + copyBackup(databaseAdminClient, projectId, instanceId, sourceBackupId, destinationBackupId); + } + } + + static void copyBackup( + DatabaseAdminClient databaseAdminClient, + String projectId, + String instanceId, + String sourceBackupId, + String destinationBackupId) { + + Timestamp expireTime = + Timestamp.ofTimeMicroseconds( + TimeUnit.MICROSECONDS.convert( + System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), + TimeUnit.MILLISECONDS)); + // Creates a copy of an existing backup. + Backup destinationBackup = + databaseAdminClient + .newBackupBuilder(BackupId.of(projectId, instanceId, destinationBackupId)) + .setExpireTime(expireTime) + .build(); + + // Initiate the request which returns an OperationFuture. + System.out.println("Copying backup [" + destinationBackup.getId() + "]..."); + OperationFuture operation = + databaseAdminClient.copyBackup( + BackupId.of(projectId, instanceId, sourceBackupId), destinationBackup); + try { + // Wait for the backup operation to complete. + destinationBackup = operation.get(); + System.out.println("Copied backup [" + destinationBackup.getId() + "]"); + } catch (ExecutionException e) { + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + throw SpannerExceptionFactory.propagateInterrupt(e); + } + // Load the metadata of the new backup from the server. + destinationBackup = destinationBackup.reload(); + System.out.println( + String.format( + "Backup %s of size %d bytes was copied at %s for version of database at %s", + destinationBackup.getId().getName(), + destinationBackup.getSize(), + LocalDateTime.ofEpochSecond( + destinationBackup.getProto().getCreateTime().getSeconds(), + destinationBackup.getProto().getCreateTime().getNanos(), + OffsetDateTime.now().getOffset()), + LocalDateTime.ofEpochSecond( + destinationBackup.getProto().getVersionTime().getSeconds(), + destinationBackup.getProto().getVersionTime().getNanos(), + OffsetDateTime.now().getOffset()))); + return; + } +} +// [END spanner_copy_backup] diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateBackupWithEncryptionKey.java similarity index 58% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateBackupWithEncryptionKey.java index 9152b26a0f..23ddd69917 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateBackupWithEncryptionKey.java @@ -14,22 +14,21 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_create_backup_with_encryption_key] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Backup; +import com.google.cloud.spanner.BackupId; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; 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.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.CreateBackupRequest; -import com.google.spanner.admin.database.v1.DatabaseName; -import com.google.spanner.admin.database.v1.InstanceName; +import com.google.cloud.spanner.encryption.EncryptionConfigs; +import com.google.spanner.admin.database.v1.CreateBackupMetadata; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -38,7 +37,7 @@ public class CreateBackupWithEncryptionKey { - static void createBackupWithEncryptionKey() { + static void createBackupWithEncryptionKey() throws InterruptedException { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; String instanceId = "my-instance"; @@ -48,8 +47,8 @@ static void createBackupWithEncryptionKey() { "projects/" + projectId + "/locations//keyRings//cryptoKeys/"; try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) { + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); createBackupWithEncryptionKey( adminClient, projectId, @@ -61,29 +60,24 @@ static void createBackupWithEncryptionKey() { } static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient, - String projectId, String instanceId, String databaseId, String backupId, String kmsKeyName) { + String projectId, String instanceId, String databaseId, String backupId, String kmsKeyName) + throws InterruptedException { // 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 Timestamp expireTime = Timestamp.ofTimeMicroseconds(TimeUnit.MICROSECONDS.convert( + System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS)); + final Backup backupToCreate = adminClient + .newBackupBuilder(BackupId.of(projectId, instanceId, backupId)) + .setDatabase(DatabaseId.of(projectId, instanceId, databaseId)) + .setExpireTime(expireTime) + .setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName)) + .build(); + final OperationFuture operation = adminClient + .createBackup(backupToCreate); - 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(); + Backup backup; try { System.out.println("Waiting for operation to complete..."); - backup = adminClient.createBackupAsync(request).get(1200, TimeUnit.SECONDS); + backup = operation.get(1200, TimeUnit.SECONDS); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw SpannerExceptionFactory.asSpannerException(e.getCause()); @@ -95,13 +89,14 @@ static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient, // 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(), + backup.getId().getName(), + backup.getSize(), LocalDateTime.ofEpochSecond( - backup.getCreateTime().getSeconds(), - backup.getCreateTime().getNanos(), + backup.getProto().getCreateTime().getSeconds(), + backup.getProto().getCreateTime().getNanos(), OffsetDateTime.now().getOffset()), kmsKeyName ); diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSample.java new file mode 100644 index 0000000000..8bfc6422cf --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSample.java @@ -0,0 +1,85 @@ +/* + * 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.archived; + +//[START spanner_create_database_with_default_leader] + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.SpannerOptions; +import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; + +public class CreateDatabaseWithDefaultLeaderSample { + + static void createDatabaseWithDefaultLeader() { + // TODO(developer): Replace these variables before running the sample. + final String projectId = "my-project"; + final String instanceId = "my-instance"; + final String databaseId = "my-database"; + final String defaultLeader = "my-default-leader"; + createDatabaseWithDefaultLeader(projectId, instanceId, databaseId, defaultLeader); + } + + static void createDatabaseWithDefaultLeader( + String projectId, String instanceId, String databaseId, String defaultLeader) { + try (Spanner spanner = SpannerOptions + .newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + final OperationFuture operation = databaseAdminClient + .createDatabase( + instanceId, + databaseId, + Arrays.asList( + "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", + "ALTER DATABASE " + "`" + databaseId + "`" + + " SET OPTIONS ( default_leader = '" + defaultLeader + "' )" + ) + ); + final Database database = operation.get(); + System.out.println("Created database [" + database.getId() + "]"); + System.out.println("\tDefault leader: " + database.getDefaultLeader()); + } 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_with_default_leader] 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/archived/CreateDatabaseWithEncryptionKey.java similarity index 60% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithEncryptionKey.java index fe0265409c..2064423547 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithEncryptionKey.java @@ -14,19 +14,20 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_create_database_with_encryption_key] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.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 com.google.cloud.spanner.encryption.EncryptionConfigs; +import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import java.util.Arrays; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -42,8 +43,8 @@ static void createDatabaseWithEncryptionKey() { "projects/" + projectId + "/locations//keyRings//cryptoKeys/"; try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) { + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); createDatabaseWithEncryptionKey( adminClient, projectId, @@ -55,35 +56,32 @@ static void createDatabaseWithEncryptionKey() { 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" - )) + final Database databaseToCreate = adminClient + .newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId)) + .setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName)) .build(); + final OperationFuture operation = adminClient + .createDatabase(databaseToCreate, Arrays.asList( + "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" + )); try { System.out.println("Waiting for operation to complete..."); - Database createdDatabase = - adminClient.createDatabaseAsync(request).get(120, TimeUnit.SECONDS); + Database createdDatabase = operation.get(120, TimeUnit.SECONDS); System.out.printf( "Database %s created with encryption key %s%n", - createdDatabase.getName(), + createdDatabase.getId(), createdDatabase.getEncryptionConfig().getKmsKeyName() ); } catch (ExecutionException e) { 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/archived/CreateDatabaseWithVersionRetentionPeriodSample.java similarity index 55% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithVersionRetentionPeriodSample.java index fbea541792..1dea5af204 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateDatabaseWithVersionRetentionPeriodSample.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_create_database_with_version_retention_period] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; -import com.google.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 com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import java.util.Arrays; import java.util.concurrent.ExecutionException; public class CreateDatabaseWithVersionRetentionPeriodSample { @@ -38,25 +38,39 @@ static void createDatabaseWithVersionRetentionPeriod() { String databaseId = "my-database"; String versionRetentionPeriod = "7d"; - createDatabaseWithVersionRetentionPeriod(projectId, instanceId, databaseId, - versionRetentionPeriod); + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + createDatabaseWithVersionRetentionPeriod(adminClient, instanceId, databaseId, + versionRetentionPeriod); + } } - static void createDatabaseWithVersionRetentionPeriod(String projectId, + static void createDatabaseWithVersionRetentionPeriod(DatabaseAdminClient adminClient, 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()) - .setCreateStatement("CREATE DATABASE `" + databaseId + "`") - .addAllExtraStatements(Lists.newArrayList("ALTER DATABASE " + "`" + databaseId + "`" - + " SET OPTIONS ( version_retention_period = '" + versionRetentionPeriod + "' )")) - .build(); - Database database = - databaseAdminClient.createDatabaseAsync(request).get(); - System.out.println("Created database [" + database.getName() + "]"); + OperationFuture op = + adminClient.createDatabase( + instanceId, + databaseId, + Arrays.asList( + "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", + "ALTER DATABASE " + "`" + databaseId + "`" + + " SET OPTIONS ( version_retention_period = '" + versionRetentionPeriod + "' )" + )); + try { + Database database = op.get(); + System.out.println("Created database [" + database.getId() + "]"); System.out.println("\tVersion retention period: " + database.getVersionRetentionPeriod()); System.out.println("\tEarliest version time: " + database.getEarliestVersionTime()); } catch (ExecutionException e) { diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceConfigSample.java new file mode 100644 index 0000000000..3b9e49bf0e --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceConfigSample.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner.admin.archived; + +// [START spanner_create_instance_config] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceConfig; +import com.google.cloud.spanner.InstanceConfigId; +import com.google.cloud.spanner.InstanceConfigInfo; +import com.google.cloud.spanner.ReplicaInfo; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.common.collect.ImmutableList; +import com.google.spanner.admin.instance.v1.CreateInstanceConfigMetadata; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +class CreateInstanceConfigSample { + static void createInstanceConfig() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String baseInstanceConfig = "my-base-instance-config"; + String instanceConfigId = "custom-instance-config4"; + createInstanceConfig(projectId, baseInstanceConfig, instanceConfigId); + } + + static void createInstanceConfig( + String projectId, String baseInstanceConfig, String instanceConfigId) { + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + final InstanceConfig baseConfig = instanceAdminClient.getInstanceConfig(baseInstanceConfig); + List readOnlyReplicas = + ImmutableList.of(baseConfig.getOptionalReplicas().get(0)); + InstanceConfigInfo instanceConfigInfo = + InstanceConfig.newBuilder(InstanceConfigId.of(projectId, instanceConfigId), baseConfig) + .setDisplayName(instanceConfigId) + .addReadOnlyReplicas(readOnlyReplicas) + .build(); + final OperationFuture operation = + instanceAdminClient.createInstanceConfig(instanceConfigInfo); + try { + System.out.printf("Waiting for create operation for %s to complete...\n", instanceConfigId); + InstanceConfig instanceConfig = operation.get(5, TimeUnit.MINUTES); + System.out.printf("Created instance configuration %s\n", instanceConfig.getId()); + } catch (ExecutionException | TimeoutException e) { + System.out.printf( + "Error: Creating instance configuration %s failed with error message %s\n", + instanceConfigInfo.getId(), e.getMessage()); + } catch (InterruptedException e) { + System.out.println( + "Error: Waiting for createInstanceConfig operation to finish was interrupted"); + } + } + } +} +// [END spanner_create_instance_config] 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/archived/CreateInstanceExample.java similarity index 56% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceExample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceExample.java index e53b46ede2..15b33ae892 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Google LLC + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,18 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; //[START spanner_create_instance] - +import com.google.api.gax.longrunning.OperationFuture; +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.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 com.google.spanner.admin.instance.v1.CreateInstanceMetadata; import java.util.concurrent.ExecutionException; class CreateInstanceExample { @@ -37,41 +38,36 @@ static void createInstance() { } static void createInstance(String projectId, String instanceId) { + Spanner spanner = SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + // Set Instance configuration. + String configId = "regional-us-central1"; int nodeCount = 2; String displayName = "Descriptive name"; - // Create an Instance object that will be used to create the instance. - Instance instance = - Instance.newBuilder() - .setDisplayName(displayName) + // Create an InstanceInfo object that will be used to create the instance. + InstanceInfo instanceInfo = + InstanceInfo.newBuilder(InstanceId.of(projectId, instanceId)) + .setInstanceConfigId(InstanceConfigId.of(projectId, configId)) .setNodeCount(nodeCount) - .setConfig( - InstanceConfigName.of(projectId, "regional-us-central1").toString()) + .setDisplayName(displayName) .build(); - - try (Spanner spanner = - SpannerOptions.newBuilder() - .setProjectId(projectId) - .build() - .getService(); - InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient()) { - + OperationFuture operation = + instanceAdminClient.createInstance(instanceInfo); + try { // Wait for the createInstance operation to finish. - Instance createdInstance = instanceAdminClient.createInstanceAsync( - CreateInstanceRequest.newBuilder() - .setParent(ProjectName.of(projectId).toString()) - .setInstanceId(instanceId) - .setInstance(instance) - .build()).get(); - System.out.printf("Instance %s was successfully created%n", createdInstance.getName()); + Instance instance = operation.get(); + System.out.printf("Instance %s was successfully created%n", instance.getId()); } catch (ExecutionException e) { System.out.printf( "Error: Creating instance %s failed with error message %s%n", - instance.getName(), e.getMessage()); + instanceInfo.getId(), e.getMessage()); } catch (InterruptedException e) { System.out.println("Error: Waiting for createInstance operation to finish was interrupted"); + } finally { + spanner.close(); } } } -//[END spanner_create_instance] \ No newline at end of file +//[END spanner_create_instance] diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigExample.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigExample.java new file mode 100644 index 0000000000..3fe60c554b --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigExample.java @@ -0,0 +1,84 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner.admin.archived; + +// [START spanner_create_instance_with_autoscaling_config] + +import com.google.api.gax.longrunning.OperationFuture; +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.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.spanner.admin.instance.v1.AutoscalingConfig; +import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; +import java.util.concurrent.ExecutionException; + +class CreateInstanceWithAutoscalingConfigExample { + + 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) { + Spanner spanner = SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + + // Set Instance configuration. + String configId = "regional-us-central1"; + // Create an autoscaling config. + AutoscalingConfig autoscalingConfig = + AutoscalingConfig.newBuilder() + .setAutoscalingLimits( + AutoscalingConfig.AutoscalingLimits.newBuilder().setMinNodes(1).setMaxNodes(2)) + .setAutoscalingTargets( + AutoscalingConfig.AutoscalingTargets.newBuilder() + .setHighPriorityCpuUtilizationPercent(65) + .setStorageUtilizationPercent(95)) + .build(); + + // Create an InstanceInfo object that will be used to create the instance. + InstanceInfo instanceInfo = + InstanceInfo.newBuilder(InstanceId.of(projectId, instanceId)) + .setInstanceConfigId(InstanceConfigId.of(projectId, configId)) + .setAutoscalingConfig(autoscalingConfig) + .setDisplayName("Descriptive name") + .build(); + OperationFuture operation = + instanceAdminClient.createInstance(instanceInfo); + + try { + // Wait for the createInstance operation to finish. + Instance instance = operation.get(); + System.out.printf("Autoscaler instance %s was successfully created%n", instance.getId()); + } catch (ExecutionException e) { + System.out.printf( + "Error: Creating instance %s failed with error message %s%n", + instanceInfo.getId(), e.getMessage()); + } catch (InterruptedException e) { + System.out.println("Error: Waiting for createInstance operation to finish was interrupted"); + } finally { + spanner.close(); + } + } +} +// [END spanner_create_instance_with_autoscaling_config] diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithProcessingUnitsExample.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithProcessingUnitsExample.java new file mode 100644 index 0000000000..f688b4cdbf --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateInstanceWithProcessingUnitsExample.java @@ -0,0 +1,76 @@ +/* + * 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.archived; + +//[START spanner_create_instance_with_processing_units] + +import com.google.api.gax.longrunning.OperationFuture; +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.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; + +class CreateInstanceWithProcessingUnitsExample { + + 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) { + Spanner spanner = SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); + InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + + // Set Instance configuration. + String configId = "regional-us-central1"; + // This will create an instance with the processing power of 0.2 nodes. + int processingUnits = 500; + String displayName = "Descriptive name"; + + try { + // Creates a new instance + System.out.printf("Creating instance %s.%n", instanceId); + OperationFuture operation = + instanceAdminClient.createInstance(InstanceInfo + .newBuilder(InstanceId.of(projectId, instanceId)) + .setInstanceConfigId(InstanceConfigId.of(projectId, configId)) + .setProcessingUnits(processingUnits) + .setDisplayName(displayName) + .build()); + + // Wait for the createInstance operation to finish. + System.out.printf("Waiting for operation on %s to complete...%n", instanceId); + Instance createdInstance = operation.get(); + + System.out.printf("Created instance %s.%n", createdInstance.getId().getInstance()); + + Instance instance = instanceAdminClient.getInstance(instanceId); + System.out.printf("Instance %s has %d processing units.%n", instance.getId().getInstance(), + instance.getProcessingUnits()); + } catch (Exception e) { + System.out.printf("Error: %s.%n", e.getMessage()); + } + spanner.close(); + } +} +//[END spanner_create_instance_with_processing_units] 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/archived/CreateSequenceSample.java similarity index 89% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateSequenceSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateSequenceSample.java index d49eacd92e..637bd39d9a 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateSequenceSample.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_create_sequence] - +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ResultSet; @@ -25,16 +25,13 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; -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.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class CreateSequenceSample { - static void createSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -44,18 +41,20 @@ static void createSequence() { } static void createSequence(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), + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + + dbAdminClient + .updateDatabaseDdl( + instanceId, + databaseId, ImmutableList.of( "CREATE SEQUENCE Seq OPTIONS (sequence_kind = 'bit_reversed_positive')", "CREATE TABLE Customers (CustomerId INT64 DEFAULT " + "(GET_NEXT_SEQUENCE_VALUE(SEQUENCE Seq)), CustomerName STRING(1024)) " - + "PRIMARY KEY (CustomerId)")) + + "PRIMARY KEY (CustomerId)"), + null) .get(5, TimeUnit.MINUTES); System.out.println( diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateTableWithForeignKeyDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateTableWithForeignKeyDeleteCascadeSample.java new file mode 100644 index 0000000000..a5cc4668d5 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/CreateTableWithForeignKeyDeleteCascadeSample.java @@ -0,0 +1,66 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner.admin.archived; + +// [START spanner_create_table_with_foreign_key_delete_cascade] +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.common.collect.ImmutableList; + +class CreateTableWithForeignKeyDeleteCascadeSample { + + static void createForeignKeyDeleteCascadeConstraint() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + createForeignKeyDeleteCascadeConstraint(adminClient, instanceId, databaseId); + } + } + + static void createForeignKeyDeleteCascadeConstraint( + DatabaseAdminClient adminClient, String instanceId, String databaseId) { + adminClient.updateDatabaseDdl( + 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"), + null); + + 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/archived/DeleteInstanceConfigSample.java similarity index 60% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/DeleteInstanceConfigSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/DeleteInstanceConfigSample.java index 479ca6f2ff..b1013cd4b0 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/DeleteInstanceConfigSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/DeleteInstanceConfigSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,15 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_delete_instance_config] - +import com.google.cloud.spanner.InstanceAdminClient; 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; class DeleteInstanceConfigSample { - static void deleteInstanceConfig() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; @@ -36,24 +32,16 @@ static void deleteInstanceConfig() { 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 = - DeleteInstanceConfigRequest.newBuilder().setName(instanceConfigName.toString()).build(); - + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); try { - System.out.printf("Deleting %s...\n", instanceConfigName); - instanceAdminClient.deleteInstanceConfig(request); - System.out.printf("Deleted instance configuration %s\n", instanceConfigName); + System.out.printf("Deleting %s...\n", instanceConfigId); + instanceAdminClient.deleteInstanceConfig(instanceConfigId); + System.out.printf("Deleted instance configuration %s\n", instanceConfigId); } catch (SpannerException e) { System.out.printf( "Error: Deleting instance configuration %s failed with error message: %s\n", - instanceConfigName, e.getMessage()); + instanceConfigId, e.getMessage()); } } } 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/archived/DropForeignKeyConstraintDeleteCascadeSample.java similarity index 58% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/DropForeignKeyConstraintDeleteCascadeSample.java index 991216a770..bf03542f91 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/DropForeignKeyConstraintDeleteCascadeSample.java @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_drop_foreign_key_constraint_delete_cascade] - +import com.google.cloud.spanner.DatabaseAdminClient; 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; class DropForeignKeyConstraintDeleteCascadeSample { @@ -32,26 +30,28 @@ static void deleteForeignKeyDeleteCascadeConstraint() { String instanceId = "my-instance"; String databaseId = "my-database"; - deleteForeignKeyDeleteCascadeConstraint(projectId, instanceId, databaseId); + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + deleteForeignKeyDeleteCascadeConstraint(adminClient, instanceId, databaseId); + } } static void deleteForeignKeyDeleteCascadeConstraint( - 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)); - } + DatabaseAdminClient adminClient, String instanceId, String databaseId) { + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of( + "ALTER TABLE ShoppingCarts\n" + + " DROP CONSTRAINT FKShoppingCartsCustomerName\n"), + null); + + 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/archived/DropSequenceSample.java similarity index 84% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/DropSequenceSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/DropSequenceSample.java index ff3522484f..3e8f2bb414 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/DropSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/DropSequenceSample.java @@ -14,22 +14,19 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_drop_sequence] - +import com.google.cloud.spanner.DatabaseAdminClient; 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.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class DropSequenceSample { - static void dropSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -40,14 +37,19 @@ static void dropSequence() { 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), + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + + dbAdminClient + .updateDatabaseDdl( + instanceId, + databaseId, ImmutableList.of( "ALTER TABLE Customers ALTER COLUMN CustomerId DROP DEFAULT", - "DROP SEQUENCE Seq")) + "DROP SEQUENCE Seq"), + null) .get(5, TimeUnit.MINUTES); + System.out.println( "Altered Customers table to drop DEFAULT from CustomerId column " + "and dropped the Seq sequence"); 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/archived/EnableFineGrainedAccess.java similarity index 63% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/EnableFineGrainedAccess.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/EnableFineGrainedAccess.java index 6538ba87d8..cc373f50f6 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/EnableFineGrainedAccess.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/EnableFineGrainedAccess.java @@ -14,21 +14,16 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_enable_fine_grained_access] - +import com.google.cloud.Binding; +import com.google.cloud.Condition; +import com.google.cloud.Policy; +import com.google.cloud.spanner.DatabaseAdminClient; 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; -import com.google.iam.v1.GetIamPolicyRequest; -import com.google.iam.v1.GetPolicyOptions; -import com.google.iam.v1.Policy; -import com.google.iam.v1.SetIamPolicyRequest; -import com.google.spanner.admin.database.v1.DatabaseName; -import com.google.type.Expr; public class EnableFineGrainedAccess { @@ -51,15 +46,12 @@ static void enableFineGrainedAccess( String title, 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); + SpannerOptions.newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + Policy policy = adminClient.getDatabaseIAMPolicy(instanceId, databaseId, 3); 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 @@ -73,17 +65,20 @@ static void enableFineGrainedAccess( Binding binding1 = Binding.newBuilder() .setRole("roles/spanner.fineGrainedAccessUser") - .addAllMembers(ImmutableList.of(iamMember)) + .setMembers(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)) + Condition.newBuilder() + .setDescription(title) + .setExpression( + String.format("resource.name.endsWith(\"/databaseRoles/%s\")", role)) + .setTitle(title) + .build()) + .setMembers(ImmutableList.of(iamMember)) .build(); ImmutableList bindings = ImmutableList.builder() @@ -95,13 +90,10 @@ static void enableFineGrainedAccess( Policy.newBuilder() .setVersion(policyVersion) .setEtag(policy.getEtag()) - .addAllBindings(bindings) + .setBindings(bindings) .build(); - final SetIamPolicyRequest setRequest = - SetIamPolicyRequest.newBuilder() - .setResource(DatabaseName.of(projectId, instanceId, databaseId).toString()) - .setPolicy(policyWithConditions).build(); - final Policy response = databaseAdminClient.setIamPolicy(setRequest); + Policy response = + adminClient.setDatabaseIAMPolicy(instanceId, databaseId, policyWithConditions); System.out.printf( "Enabled fine-grained access in IAM with version %d%n", response.getVersion()); } 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/archived/GetDatabaseDdlSample.java similarity index 67% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/GetDatabaseDdlSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/GetDatabaseDdlSample.java index 5e666ee4f1..c7f56f4969 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetDatabaseDdlSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/GetDatabaseDdlSample.java @@ -14,15 +14,14 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; //[START spanner_get_database_ddl] +import com.google.cloud.spanner.DatabaseAdminClient; 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.util.List; public class GetDatabaseDdlSample { @@ -36,13 +35,15 @@ static void getDatabaseDdl() { static void getDatabaseDdl( 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)); + try (Spanner spanner = SpannerOptions + .newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + final List ddls = databaseAdminClient.getDatabaseDdl(instanceId, databaseId); System.out.println("Retrieved database DDL for " + databaseId); - for (String ddl : response.getStatementsList()) { + for (String ddl : ddls) { 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/archived/GetInstanceConfigSample.java similarity index 54% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/GetInstanceConfigSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/GetInstanceConfigSample.java index 334741c3d3..38b6230b97 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/GetInstanceConfigSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/GetInstanceConfigSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * 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. @@ -14,42 +14,39 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; //[START spanner_get_instance_config] +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceConfig; 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; public class GetInstanceConfigSample { static void getInstanceConfig() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; - final String instanceConfigId = "nam6"; - getInstanceConfig(projectId, instanceConfigId); + final String instanceConfigName = "nam6"; + getInstanceConfig(projectId, instanceConfigName); } - 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); + static void getInstanceConfig(String projectId, String instanceConfigName) { + try (Spanner spanner = SpannerOptions + .newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); - final InstanceConfig instanceConfig = - instanceAdminClient.getInstanceConfig(instanceConfigName.toString()); + final InstanceConfig instanceConfig = instanceAdminClient + .getInstanceConfig(instanceConfigName); System.out.printf( "Available leader options for instance config %s: %s%n", - instanceConfig.getName(), - instanceConfig.getLeaderOptionsList() + instanceConfig.getId(), + instanceConfig.getLeaderOptions() ); } } 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/archived/ListDatabaseRoles.java similarity index 54% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabaseRoles.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/ListDatabaseRoles.java index 7fea5a9c55..ee31d874ec 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabaseRoles.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListDatabaseRoles.java @@ -14,21 +14,19 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_list_database_roles] - +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.DatabaseRole; 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.util.concurrent.ExecutionException; public class ListDatabaseRoles { - static void listDatabaseRoles() { + static void listDatabaseRoles() throws InterruptedException, ExecutionException { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; String instanceId = "my-instance"; @@ -38,16 +36,15 @@ static void listDatabaseRoles() { 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); + SpannerOptions.newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + String databasePath = DatabaseId.of(projectId, instanceId, databaseId).getName(); 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()); - } + for (DatabaseRole role : adminClient.listDatabaseRoles(instanceId, databaseId).iterateAll()) { + System.out.printf("%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/archived/ListDatabasesSample.java similarity index 61% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabasesSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/ListDatabasesSample.java index 703e89b9f1..4abd2c6f25 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListDatabasesSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListDatabasesSample.java @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; //[START spanner_list_databases] +import com.google.api.gax.paging.Page; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; 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; public class ListDatabasesSample { @@ -36,20 +34,21 @@ static void listDatabases() { } 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)); - + try (Spanner spanner = SpannerOptions + .newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + Page page = databaseAdminClient.listDatabases(instanceId); System.out.println("Databases for projects/" + projectId + "/instances/" + instanceId); - - for (ListDatabasesPage page : response.iteratePages()) { + while (page != null) { for (Database database : page.iterateAll()) { final String defaultLeader = database.getDefaultLeader().equals("") ? "" : "(default leader = " + database.getDefaultLeader() + ")"; - System.out.println("\t" + database.getName() + " " + defaultLeader); + System.out.println("\t" + database.getId() + " " + defaultLeader); } + page = page.getNextPage(); } } } diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListInstanceConfigOperationsSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListInstanceConfigOperationsSample.java new file mode 100644 index 0000000000..2cf683b4c9 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListInstanceConfigOperationsSample.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner.admin.archived; + +// [START spanner_list_instance_config_operations] +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.Options; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.longrunning.Operation; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.spanner.admin.instance.v1.CreateInstanceConfigMetadata; + +public class ListInstanceConfigOperationsSample { + static void listInstanceConfigOperations() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + listInstanceConfigOperations(projectId); + } + + static void listInstanceConfigOperations(String projectId) { + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + + try { + System.out.printf( + "Getting list of instance config operations for project %s...\n", + projectId); + final Iterable instanceConfigOperations = + instanceAdminClient + .listInstanceConfigOperations( + Options.filter( + "(metadata.@type=type.googleapis.com/" + + "google.spanner.admin.instance.v1.CreateInstanceConfigMetadata)")) + .iterateAll(); + for (Operation operation : instanceConfigOperations) { + CreateInstanceConfigMetadata metadata = + operation.getMetadata().unpack(CreateInstanceConfigMetadata.class); + System.out.printf( + "Create instance config operation for %s is %d%% completed.\n", + metadata.getInstanceConfig().getName(), metadata.getProgress().getProgressPercent()); + } + } catch (InvalidProtocolBufferException e) { + System.out.printf( + "Error: Listing instance config operations failed with error message %s\n", + e.getMessage()); + } + } + } +} +// [END spanner_list_instance_config_operations] 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/archived/ListInstanceConfigsSample.java similarity index 57% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigsSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/ListInstanceConfigsSample.java index f2d6f5fba6..b7753502ba 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigsSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/ListInstanceConfigsSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * 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. @@ -14,38 +14,36 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; //[START spanner_list_instance_configs] +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceConfig; 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; public class ListInstanceConfigsSample { static void listInstanceConfigs() { // TODO(developer): Replace these variables before running the sample. - String projectId = "my-project"; + final String projectId = "my-project"; listInstanceConfigs(projectId); } 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()) { + try (Spanner spanner = SpannerOptions + .newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + + for (InstanceConfig instanceConfig : instanceAdminClient.listInstanceConfigs().iterateAll()) { System.out.printf( "Available leader options for instance config %s: %s%n", - instanceConfig.getName(), - instanceConfig.getLeaderOptionsList() + instanceConfig.getId(), + instanceConfig.getLeaderOptions() ); } } 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/archived/PgAlterSequenceSample.java similarity index 89% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/PgAlterSequenceSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/PgAlterSequenceSample.java index 1168bdcfad..10bccc2cb4 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgAlterSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/PgAlterSequenceSample.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_postgresql_alter_sequence] - +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ResultSet; @@ -25,16 +25,13 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; -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.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class PgAlterSequenceSample { - static void pgAlterSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -45,13 +42,14 @@ static void pgAlterSequence() { static void pgAlterSequence(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 SEQUENCE Seq SKIP RANGE 1000 5000000")) + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + dbAdminClient + .updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of("ALTER SEQUENCE Seq SKIP RANGE 1000 5000000"), + null) .get(5, TimeUnit.MINUTES); System.out.println( "Altered Seq sequence to skip an inclusive range between 1000 and 5000000"); 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/archived/PgCaseSensitivitySample.java similarity index 79% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCaseSensitivitySample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/PgCaseSensitivitySample.java index 8fbaf6a9c0..a9096c0c2e 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCaseSensitivitySample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/PgCaseSensitivitySample.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_postgresql_identifier_case_sensitivity] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Mutation; @@ -26,9 +28,7 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; -import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient; -import com.google.common.collect.Lists; -import com.google.spanner.admin.database.v1.DatabaseName; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import java.util.Collections; import java.util.concurrent.ExecutionException; @@ -43,28 +43,34 @@ static void pgCaseSensitivity() { } static void pgCaseSensitivity(String projectId, String instanceId, String databaseId) { - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + SpannerOptions.newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); // Spanner PostgreSQL follows the case sensitivity rules of PostgreSQL. This means that: // 1. Identifiers that are not double-quoted are folded to lower case. // 2. Identifiers that are double-quoted retain their case and are case-sensitive. // See https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS // for more information. - databaseAdminClient.updateDatabaseDdlAsync( - DatabaseName.of(projectId, instanceId, databaseId), - Lists.newArrayList( - "CREATE TABLE Singers (" - // SingerId will be folded to `singerid`. - + " SingerId bigint NOT NULL PRIMARY KEY," - // FirstName and LastName are double-quoted and will therefore retain their - // mixed case and are case-sensitive. This means that any statement that - // references any of these columns must use double quotes. - + " \"FirstName\" varchar(1024) NOT NULL," - + " \"LastName\" varchar(1024) NOT NULL" - + ")")).get(); + final OperationFuture updateOperation = + databaseAdminClient.updateDatabaseDdl( + instanceId, + databaseId, + Collections.singleton( + "CREATE TABLE Singers (" + // SingerId will be folded to `singerid`. + + " SingerId bigint NOT NULL PRIMARY KEY," + // FirstName and LastName are double-quoted and will therefore retain their + // mixed case and are case-sensitive. This means that any statement that + // references any of these columns must use double quotes. + + " \"FirstName\" varchar(1024) NOT NULL," + + " \"LastName\" varchar(1024) NOT NULL" + + ")"), + null); + updateOperation.get(); DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); 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/archived/PgCreateSequenceSample.java similarity index 88% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCreateSequenceSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/PgCreateSequenceSample.java index 2148427ccd..070f62f70f 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgCreateSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/PgCreateSequenceSample.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_postgresql_create_sequence] - +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ResultSet; @@ -25,16 +25,13 @@ import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; -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.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class PgCreateSequenceSample { - static void pgCreateSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -45,18 +42,22 @@ static void pgCreateSequence() { static void pgCreateSequence(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).toString(), + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + + dbAdminClient + .updateDatabaseDdl( + instanceId, + databaseId, ImmutableList.of( "CREATE SEQUENCE Seq BIT_REVERSED_POSITIVE;", "CREATE TABLE Customers (CustomerId BIGINT DEFAULT nextval('Seq'), " - + "CustomerName character varying(1024), PRIMARY KEY (CustomerId))")) + + "CustomerName character varying(1024), PRIMARY KEY (CustomerId))"), + null) .get(5, TimeUnit.MINUTES); System.out.println( - "Created Seq sequence and Customers table, where the key column " + "Created Seq sequence and Customers table, where its key column " + "CustomerId uses the sequence as a default value"); final DatabaseClient dbClient = 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/archived/PgDropSequenceSample.java similarity index 84% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/PgDropSequenceSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/PgDropSequenceSample.java index de856f51a3..26e8eb74bb 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgDropSequenceSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/PgDropSequenceSample.java @@ -14,22 +14,19 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_postgresql_drop_sequence] - +import com.google.cloud.spanner.DatabaseAdminClient; 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.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class PgDropSequenceSample { - static void pgDropSequence() { // TODO(developer): Replace these variables before running the sample. final String projectId = "my-project"; @@ -39,16 +36,17 @@ static void pgDropSequence() { } static void pgDropSequence(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), + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + dbAdminClient + .updateDatabaseDdl( + instanceId, + databaseId, ImmutableList.of( "ALTER TABLE Customers ALTER COLUMN CustomerId DROP DEFAULT", - "DROP SEQUENCE Seq")) + "DROP SEQUENCE Seq"), + null) .get(5, TimeUnit.MINUTES); System.out.println( "Altered Customers table to drop DEFAULT from " 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/archived/PgInterleavedTableSample.java similarity index 62% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/PgInterleavedTableSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/PgInterleavedTableSample.java index 754920c8dd..007843a4a2 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgInterleavedTableSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/PgInterleavedTableSample.java @@ -14,15 +14,16 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_postgresql_interleaved_table] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; 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 com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import java.util.Arrays; import java.util.concurrent.ExecutionException; @@ -37,29 +38,35 @@ static void pgInterleavedTable() { } static void pgInterleavedTable(String projectId, String instanceId, String databaseId) { - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { + SpannerOptions.newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + // 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 // for the full CREATE TABLE syntax. - databaseAdminClient.updateDatabaseDdlAsync(DatabaseName.of(projectId, + final OperationFuture updateOperation = + databaseAdminClient.updateDatabaseDdl( instanceId, - databaseId), - Arrays.asList( - "CREATE TABLE Singers (" - + " SingerId bigint NOT NULL PRIMARY KEY," - + " FirstName varchar(1024) NOT NULL," - + " LastName varchar(1024) NOT NULL" - + ")", - "CREATE TABLE Albums (" - + " SingerId bigint NOT NULL," - + " AlbumId bigint NOT NULL," - + " Title varchar(1024) NOT NULL," - + " PRIMARY KEY (SingerId, AlbumId)" - + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE")).get(); + databaseId, + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL PRIMARY KEY," + + " FirstName varchar(1024) NOT NULL," + + " LastName varchar(1024) NOT NULL" + + ")", + "CREATE TABLE Albums (" + + " SingerId bigint NOT NULL," + + " AlbumId bigint NOT NULL," + + " Title varchar(1024) NOT NULL," + + " PRIMARY KEY (SingerId, AlbumId)" + + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), + null); + updateOperation.get(); System.out.println("Created interleaved table hierarchy using PostgreSQL dialect"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. 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/archived/PgSpannerSample.java similarity index 80% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/PgSpannerSample.java index c4e319c66c..8a95cebef0 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/PgSpannerSample.java @@ -14,18 +14,26 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; +import com.google.api.gax.longrunning.OperationFuture; import com.google.api.gax.paging.Page; import com.google.cloud.ByteArray; import com.google.cloud.Date; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceId; import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ReadOnlyTransaction; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.Spanner; @@ -37,27 +45,19 @@ 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.CreateDatabaseMetadata; import com.google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata; -import com.google.spanner.admin.instance.v1.InstanceName; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import com.google.spanner.v1.ExecuteSqlRequest; import java.math.BigDecimal; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -66,7 +66,6 @@ * Example code for using the Cloud Spanner PostgreSQL interface. */ public class PgSpannerSample { - // [START spanner_postgresql_insert_data] static final List SINGERS = Arrays.asList( @@ -84,9 +83,7 @@ public class PgSpannerSample { new Album(2, 3, "Terrified")); // [END spanner_postgresql_insert_data] - /** - * Class to contain performance sample data. - */ + /** Class to contain performance sample data. */ static class Performance { final long singerId; @@ -162,9 +159,7 @@ static class Performance { new BigDecimal("390650.99"))); // [END spanner_postgresql_insert_datatypes_data] - /** - * Class to contain venue sample data. - */ + /** Class to contain venue sample data. */ static class Venue { final long venueId; @@ -200,18 +195,15 @@ static class Venue { } // [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(); - + static void createPostgreSqlDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = dbAdminClient.createDatabase( + dbAdminClient.newDatabaseBuilder(id).setDialect(Dialect.POSTGRESQL).build(), + Collections.emptyList()); try { // Initiate the request which returns an OperationFuture. - Database db = dbAdminClient.createDatabaseAsync(request).get(); - System.out.println("Created database [" + db.getName() + "]"); + Database db = op.get(); + System.out.println("Created database [" + db.getId() + "]"); + createTableUsingDdl(dbAdminClient, id); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw (SpannerException) e.getCause(); @@ -280,9 +272,9 @@ static void deleteExampleData(DatabaseClient dbClient) { // [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"))) { + 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), @@ -295,12 +287,12 @@ static void query(DatabaseClient dbClient) { // [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"))) { + 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), @@ -311,12 +303,15 @@ static void read(DatabaseClient dbClient) { // [END spanner_postgresql_read_data] // [START spanner_postgresql_add_column] - static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseName databaseName) { + static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget bigint"), + null); try { // Initiate the request which returns an OperationFuture. - adminClient.updateDatabaseDdlAsync( - databaseName, - Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget bigint")).get(); + op.get(); System.out.println("Added MarketingBudget column"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. @@ -409,11 +404,11 @@ static void queryMarketingBudget(DatabaseClient dbClient) { // 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"))) { + 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", @@ -429,12 +424,16 @@ static void queryMarketingBudget(DatabaseClient dbClient) { // [END spanner_postgresql_query_data_with_new_column] // [START spanner_postgresql_create_index] - static void addIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) { + static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), + null); try { // Initiate the request which returns an OperationFuture. - adminClient.updateDatabaseDdlAsync( - databaseName, - Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)")).get(); + op.get(); System.out.println("Added AlbumsByAlbumTitle index"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. @@ -450,13 +449,13 @@ static void addIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) // [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"))) { + 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)); } @@ -465,14 +464,17 @@ static void readUsingIndex(DatabaseClient dbClient) { // [END spanner_postgresql_read_data_with_index] // [START spanner_postgresql_create_storing_index] - static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) { + static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList( + "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) " + + "INCLUDE (MarketingBudget)"), + null); try { // Initiate the request which returns an OperationFuture. - adminClient.updateDatabaseDdlAsync( - databaseName, - Arrays.asList( - "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) " - + "INCLUDE (MarketingBudget)")).get(); + op.get(); System.out.println("Added AlbumsByAlbumTitle2 index"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. @@ -491,13 +493,13 @@ static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseName databa 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"))) { + dbClient + .singleUse() + .readUsingIndex( + "Albums", + "AlbumsByAlbumTitle2", + KeySet.all(), + Arrays.asList("AlbumId", "AlbumTitle", "MarketingBudget"))) { while (resultSet.next()) { System.out.printf( "%d %s %s\n", @@ -524,8 +526,8 @@ static void readOnlyTransaction(DatabaseClient dbClient) { queryResultSet.getString(2)); } try (ResultSet readResultSet = - transaction.read( - "Albums", KeySet.all(), Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) { + transaction.read( + "Albums", KeySet.all(), Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) { while (readResultSet.next()) { System.out.printf( "%d %d %s\n", @@ -540,10 +542,10 @@ static void readOnlyTransaction(DatabaseClient dbClient) { // [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"))) { + 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", @@ -654,31 +656,35 @@ static void writeWithTransactionUsingDml(DatabaseClient dbClient) { // [START spanner_postgresql_create_table_using_ddl] // [START spanner_postgresql_create_database] - static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseName databaseName) { + static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL," + + " FirstName character varying(1024)," + + " LastName character varying(1024)," + + " SingerInfo bytea," + + " 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"), + null); 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 + "]"); + op.get(); + System.out.println("Created Singers & Albums tables in database: [" + id + "]"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. - throw SpannerExceptionFactory.asSpannerException(e); + 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. @@ -691,11 +697,11 @@ static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseName // [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"))) { + 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", @@ -743,14 +749,17 @@ static void updateWithTimestamp(DatabaseClient dbClient) { // [END spanner_postgresql_update_data_with_timestamp_column] // [START spanner_postgresql_add_timestamp_column] - static void addLastUpdateTimestampColumn( - DatabaseAdminClient adminClient, DatabaseName databaseName) { + static void addLastUpdateTimestampColumn(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList( + "ALTER TABLE Albums ADD COLUMN LastUpdateTime spanner.commit_timestamp"), + null); try { // Initiate the request which returns an OperationFuture. - adminClient.updateDatabaseDdlAsync( - databaseName, - Arrays.asList( - "ALTER TABLE Albums ADD COLUMN LastUpdateTime spanner.commit_timestamp")).get(); + op.get(); System.out.println("Added LastUpdateTime as a timestamp column in Albums table."); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. @@ -769,14 +778,14 @@ static void queryMarketingBudgetWithTimestamp(DatabaseClient dbClient) { // 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"))) { + 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", @@ -792,20 +801,24 @@ static void queryMarketingBudgetWithTimestamp(DatabaseClient dbClient) { // [END spanner_postgresql_query_data_with_timestamp_column] // [START spanner_postgresql_create_table_with_timestamp_column] - static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, - DatabaseName databaseName) { + static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Performances (" + + " SingerId BIGINT NOT NULL," + + " VenueId BIGINT NOT NULL," + + " Revenue BIGINT," + + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," + + " PRIMARY KEY (SingerId, VenueId))" + + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), + null); try { // Initiate the request which returns an OperationFuture. - 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 + "]"); + op.get(); + System.out.println("Created Performances table in database: [" + id + "]"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw (SpannerException) e.getCause(); @@ -842,13 +855,13 @@ static void queryPerformancesTable(DatabaseClient dbClient) { // 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"))) { + 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", @@ -981,24 +994,27 @@ static void updateUsingBatchDml(DatabaseClient dbClient) { // [END spanner_postgresql_dml_batch_update] // [START spanner_postgresql_create_table_with_datatypes] - static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, - DatabaseName databaseName) { + static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Venues (" + + " VenueId BIGINT NOT NULL," + + " VenueName character varying(100)," + + " VenueInfo bytea," + + " Capacity BIGINT," + + " OutdoorVenue BOOL, " + + " PopularityScore FLOAT8, " + + " Revenue NUMERIC, " + + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," + + " PRIMARY KEY (VenueId))"), + null); try { // Initiate the request which returns an OperationFuture. - 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 + "]"); + op.get(); + System.out.println("Created Venues table in database: [" + id + "]"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw (SpannerException) e.getCause(); @@ -1206,9 +1222,9 @@ static void clientWithQueryOptions(DatabaseId db) { Spanner spanner = options.getService(); DatabaseClient dbClient = spanner.getDatabaseClient(db); try (ResultSet resultSet = - dbClient - .singleUse() - .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) { + 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)); @@ -1220,19 +1236,19 @@ static void clientWithQueryOptions(DatabaseId db) { // [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())) { + 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)); @@ -1242,26 +1258,22 @@ static void queryWithQueryOptions(DatabaseClient dbClient) { // [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. + static void listBackupOperations(InstanceAdminClient instanceAdminClient, DatabaseId databaseId) { + Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); + // Get create backup operations for the sample database. + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); String filter = String.format( - "(metadata.@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()) { + "(metadata.database:%s) AND " + + "(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.CreateBackupMetadata) AND " + + "(metadata.progress.start_time > \"%s\")", + databaseId.getName(), last24Hours); + Page operations = instance + .listBackupOperations(Options.filter(filter)); + for (com.google.longrunning.Operation op : operations.iterateAll()) { try { CreateBackupMetadata metadata = op.getMetadata().unpack(CreateBackupMetadata.class); System.out.println( @@ -1275,55 +1287,23 @@ static void listBackupOperations( 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) { + InstanceAdminClient instanceAdminClient, + DatabaseAdminClient dbAdminClient, + InstanceId instanceId) { + Instance instance = instanceAdminClient.getInstance(instanceId.getInstance()); // 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); + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); String filter = String.format("(metadata.@type:type.googleapis.com/" + "google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata) AND " + "(metadata.progress.start_time > \"%s\")", last24Hours); - 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()) { + for (Operation op : instance.listDatabaseOperations(Options.filter(filter)).iterateAll()) { try { OptimizeRestoredDatabaseMetadata metadata = op.getMetadata().unpack(OptimizeRestoredDatabaseMetadata.class); @@ -1342,15 +1322,12 @@ static void listDatabaseOperations( static void run( DatabaseClient dbClient, DatabaseAdminClient dbAdminClient, + InstanceAdminClient instanceAdminClient, String command, - DatabaseId database, - String backupId) { - DatabaseName databaseName = DatabaseName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase()); + DatabaseId database) { switch (command) { - case "createpgdatabase": - createPostgreSqlDatabase(dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase()); + case "createdatabase": + createPostgreSqlDatabase(dbAdminClient, database); break; case "write": writeExampleData(dbClient); @@ -1365,7 +1342,7 @@ static void run( read(dbClient); break; case "addmarketingbudget": - addMarketingBudget(dbAdminClient, databaseName); + addMarketingBudget(dbAdminClient, database); break; case "update": update(dbClient); @@ -1377,13 +1354,13 @@ static void run( queryMarketingBudget(dbClient); break; case "addindex": - addIndex(dbAdminClient, databaseName); + addIndex(dbAdminClient, database); break; case "readindex": readUsingIndex(dbClient); break; case "addstoringindex": - addStoringIndex(dbAdminClient, databaseName); + addStoringIndex(dbAdminClient, database); break; case "readstoringindex": readStoringIndex(dbClient); @@ -1404,13 +1381,13 @@ static void run( writeWithTransactionUsingDml(dbClient); break; case "createtableusingddl": - createTableUsingDdl(dbAdminClient, databaseName); + createTableUsingDdl(dbAdminClient, database); break; case "readstaledata": readStaleData(dbClient); break; case "addlastupdatetimestampcolumn": - addLastUpdateTimestampColumn(dbAdminClient, databaseName); + addLastUpdateTimestampColumn(dbAdminClient, database); break; case "updatewithtimestamp": updateWithTimestamp(dbClient); @@ -1419,7 +1396,7 @@ static void run( queryMarketingBudgetWithTimestamp(dbClient); break; case "createtablewithtimestamp": - createTableWithTimestamp(dbAdminClient, databaseName); + createTableWithTimestamp(dbAdminClient, database); break; case "writewithtimestamp": writeExampleDataWithTimestamp(dbClient); @@ -1449,7 +1426,7 @@ static void run( updateUsingBatchDml(dbClient); break; case "createtablewithdatatypes": - createTableWithDatatypes(dbAdminClient, databaseName); + createTableWithDatatypes(dbAdminClient, database); break; case "writedatatypesdata": writeDatatypesData(dbClient); @@ -1482,12 +1459,10 @@ static void run( queryWithQueryOptions(dbClient); break; case "listbackupoperations": - listBackupOperations(dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase(), backupId); + listBackupOperations(instanceAdminClient, database); break; case "listdatabaseoperations": - listDatabaseOperations(dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance()); + listDatabaseOperations(instanceAdminClient, dbAdminClient, database.getInstanceId()); break; default: printUsageAndExit(); @@ -1550,10 +1525,9 @@ public static void main(String[] args) { // [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]; + 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. @@ -1565,35 +1539,23 @@ public static void main(String[] args) { + 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 = spanner.createDatabaseAdminClient(); + DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); // [END spanner_init_client] // Use client here... - run(dbClient, dbAdminClient, command, db, backupId); + run(dbClient, dbAdminClient, instanceAdminClient, command, db); // [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. - */ + /** Class to contain singer sample data. */ static class Singer { final long singerId; @@ -1607,9 +1569,7 @@ static class Singer { } } - /** - * Class to contain album sample data. - */ + /** Class to contain album sample data. */ static class Album { final long singerId; 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/archived/RestoreBackupWithEncryptionKey.java similarity index 65% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/RestoreBackupWithEncryptionKey.java index 3c129860f0..087413dbe5 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/RestoreBackupWithEncryptionKey.java @@ -14,21 +14,21 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_restore_backup_with_encryption_key] -import static com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION; - +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.BackupId; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Restore; 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.InstanceName; -import com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig; -import com.google.spanner.admin.database.v1.RestoreDatabaseRequest; +import com.google.cloud.spanner.encryption.EncryptionConfigs; +import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata; import java.util.concurrent.ExecutionException; public class RestoreBackupWithEncryptionKey { @@ -43,8 +43,8 @@ static void restoreBackupWithEncryptionKey() { "projects/" + projectId + "/locations//keyRings//cryptoKeys/"; try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - DatabaseAdminClient adminClient = spanner.createDatabaseAdminClient()) { + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); restoreBackupWithEncryptionKey( adminClient, projectId, @@ -57,18 +57,19 @@ static void restoreBackupWithEncryptionKey() { 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(); + final Restore restore = adminClient + .newRestoreBuilder( + BackupId.of(projectId, instanceId, backupId), + DatabaseId.of(projectId, instanceId, restoreId)) + .setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName)) + .build(); + final OperationFuture operation = adminClient + .restoreDatabase(restore); + Database database; try { System.out.println("Waiting for operation to complete..."); - database = adminClient.restoreDatabaseAsync(request).get(); - ; + database = operation.get(); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw SpannerExceptionFactory.asSpannerException(e.getCause()); @@ -80,9 +81,9 @@ static Void restoreBackupWithEncryptionKey(DatabaseAdminClient adminClient, 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.getRestoreInfo().getSourceDatabase(), + database.getId(), + database.getRestoreInfo().getBackup(), database.getEncryptionConfig().getKmsKeyName() ); return null; 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/archived/SpannerSample.java similarity index 79% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/SpannerSample.java index 7cc4af7c74..98eba6d944 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/SpannerSample.java @@ -14,25 +14,34 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; 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.paging.Page; 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.Timestamp; +import com.google.cloud.spanner.Backup; +import com.google.cloud.spanner.BackupId; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceId; import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ReadOnlyTransaction; +import com.google.cloud.spanner.RestoreInfo; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerBatchUpdateException; @@ -44,41 +53,27 @@ 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.CreateDatabaseMetadata; 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.admin.database.v1.UpdateDatabaseDdlMetadata; 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; +import org.threeten.bp.LocalDate; +import org.threeten.bp.LocalDateTime; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.temporal.ChronoField; /** * Example code for using the Cloud Spanner API. This example demonstrates all the common operations @@ -98,9 +93,7 @@ */ public class SpannerSample { - /** - * Class to contain singer sample data. - */ + /** Class to contain singer sample data. */ static class Singer { final long singerId; @@ -114,9 +107,7 @@ static class Singer { } } - /** - * Class to contain album sample data. - */ + /** Class to contain album sample data. */ static class Album { final long singerId; @@ -130,9 +121,7 @@ static class Album { } } - /** - * Class to contain performance sample data. - */ + /** Class to contain performance sample data. */ static class Performance { final long singerId; @@ -148,9 +137,7 @@ static class Performance { } } - /** - * Class to contain venue sample data. - */ + /** Class to contain venue sample data. */ static class Venue { final long venueId; @@ -188,6 +175,17 @@ static class Venue { } } + /** Get a database id to restore a backup to from the sample database id. */ + static String createRestoredSampleDbId(DatabaseId database) { + int index = database.getDatabase().indexOf('-'); + String prefix = database.getDatabase().substring(0, index); + String restoredDbId = database.getDatabase().replace(prefix, "restored"); + if (restoredDbId.length() > 30) { + restoredDbId = restoredDbId.substring(0, 30); + } + return restoredDbId; + } + // [START spanner_insert_data] static final List SINGERS = Arrays.asList( @@ -274,13 +272,12 @@ static class Venue { // [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( + static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.createDatabase( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( "CREATE TABLE Singers (" + " SingerId INT64 NOT NULL," + " FirstName STRING(1024)," @@ -294,12 +291,11 @@ static void createDatabase(DatabaseAdminClient dbAdminClient, + " AlbumId INT64 NOT NULL," + " AlbumTitle STRING(MAX)" + ") PRIMARY KEY (SingerId, AlbumId)," - + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")).build(); + + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")); 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() + "]"); + Database db = op.get(); + System.out.println("Created database [" + db.getId() + "]"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw (SpannerException) e.getCause(); @@ -312,23 +308,25 @@ static void createDatabase(DatabaseAdminClient dbAdminClient, // [END spanner_create_database] // [START spanner_create_table_with_timestamp_column] - static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, - DatabaseName databaseName) { + static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + 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"), + null); 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() + "]"); + op.get(); + System.out.println("Created Performances table in database: [" + id + "]"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw (SpannerException) e.getCause(); @@ -448,12 +446,16 @@ static void read(DatabaseClient dbClient) { // [END spanner_read_data] // [START spanner_add_column] - static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseName databaseName) { + static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget INT64"), + null); try { // Initiate the request which returns an OperationFuture. - adminClient.updateDatabaseDdlAsync( - databaseName, - Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget INT64")).get(); + op.get(); System.out.println("Added MarketingBudget column"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. @@ -563,12 +565,16 @@ static void queryMarketingBudget(DatabaseClient dbClient) { // [END spanner_query_data_with_new_column] // [START spanner_create_index] - static void addIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) { + static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), + null); try { // Initiate the request which returns an OperationFuture. - adminClient.updateDatabaseDdlAsync( - databaseName, - Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)")).get(); + op.get(); System.out.println("Added AlbumsByAlbumTitle index"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. @@ -630,14 +636,18 @@ static void readUsingIndex(DatabaseClient dbClient) { // [END spanner_read_data_with_index] // [START spanner_create_storing_index] - static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseName databaseName) { + static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList( + "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) " + + "STORING (MarketingBudget)"), + null); try { // Initiate the request which returns an OperationFuture. - adminClient.updateDatabaseDdlAsync( - databaseName, - Arrays.asList( - "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) " - + "STORING (MarketingBudget)")).get(); + op.get(); System.out.println("Added AlbumsByAlbumTitle2 index"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. @@ -719,14 +729,18 @@ static void readStaleData(DatabaseClient dbClient) { // [END spanner_read_stale_data] // [START spanner_add_timestamp_column] - static void addCommitTimestamp(DatabaseAdminClient adminClient, DatabaseName databaseName) { + static void addCommitTimestamp(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList( + "ALTER TABLE Albums ADD COLUMN LastUpdateTime TIMESTAMP " + + "OPTIONS (allow_commit_timestamp=true)"), + null); 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(); + op.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. @@ -1172,18 +1186,18 @@ static void writeWithTransactionUsingDml(DatabaseClient dbClient) { album2Budget -= transfer; Statement updateStatement = Statement.newBuilder( - "UPDATE Albums " - + "SET MarketingBudget = @AlbumBudget " - + "WHERE SingerId = 1 and AlbumId = 1") + "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") + "UPDATE Albums " + + "SET MarketingBudget = @AlbumBudget " + + "WHERE SingerId = 2 and AlbumId = 2") .bind("AlbumBudget") .to(album2Budget) .build(); @@ -1241,26 +1255,30 @@ static void updateUsingBatchDml(DatabaseClient dbClient) { // [END spanner_dml_batch_update] // [START spanner_create_table_with_datatypes] - static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, - DatabaseName databaseName) { + static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + 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)"), + null); 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() + "]"); + op.get(); + System.out.println("Created Venues table in database: [" + id + "]"); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw (SpannerException) e.getCause(); @@ -1552,76 +1570,74 @@ static void queryWithQueryOptions(DatabaseClient dbClient) { // [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) { + static void createBackup(DatabaseAdminClient dbAdminClient, DatabaseId databaseId, + BackupId 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(); - + Timestamp expireTime = Timestamp.ofTimeMicroseconds(TimeUnit.MICROSECONDS.convert( + System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS)); + Backup backup = + dbAdminClient + .newBackupBuilder(backupId) + .setDatabase(databaseId) + .setExpireTime(expireTime) + .setVersionTime(versionTime) + .build(); // Initiate the request which returns an OperationFuture. - System.out.println("Creating backup [" + backupId + "]..."); + System.out.println("Creating backup [" + backup.getId() + "]..."); + OperationFuture op = backup.create(); 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() + "]"); + backup = op.get(); + System.out.println("Created backup [" + backup.getId() + "]"); } catch (ExecutionException e) { - throw SpannerExceptionFactory.asSpannerException(e); + throw (SpannerException) e.getCause(); } catch (InterruptedException e) { throw SpannerExceptionFactory.propagateInterrupt(e); } // Reload the metadata of the backup from the server. - backup = dbAdminClient.getBackup(backup.getName()); + backup = backup.reload(); 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())) - ); + backup.getId().getName(), + backup.getSize(), + LocalDateTime.ofEpochSecond( + backup.getProto().getCreateTime().getSeconds(), + backup.getProto().getCreateTime().getNanos(), + OffsetDateTime.now().getOffset()), + LocalDateTime.ofEpochSecond( + backup.getProto().getVersionTime().getSeconds(), + backup.getProto().getVersionTime().getNanos(), + OffsetDateTime.now().getOffset()) + )); } // [END spanner_create_backup] // [START spanner_cancel_backup_create] static void cancelCreateBackup( - DatabaseAdminClient dbAdminClient, String projectId, String instanceId, - String databaseId, String backupId) { + DatabaseAdminClient dbAdminClient, DatabaseId databaseId, BackupId 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(); - + Timestamp expireTime = Timestamp.ofTimeMicroseconds(TimeUnit.MICROSECONDS.convert( + System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS)); + + // Create a backup instance. + Backup backup = + dbAdminClient + .newBackupBuilder(backupId) + .setDatabase(databaseId) + .setExpireTime(expireTime) + .build(); + // Start the creation of a backup. + System.out.println("Creating backup [" + backup.getId() + "]..."); + OperationFuture op = backup.create(); 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()); - + System.out.println("Cancelling create backup operation for [" + backup.getId() + "]..."); + dbAdminClient.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. @@ -1631,11 +1647,11 @@ static void cancelCreateBackup( } 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 + backup.delete(); + System.out.println("Backup operation for [" + backup.getId() + "] 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"); + System.out.println("Backup operation for [" + backup.getId() + "] successfully cancelled"); } } catch (ExecutionException e) { throw SpannerExceptionFactory.newSpannerException(e.getCause()); @@ -1647,22 +1663,20 @@ static void cancelCreateBackup( // [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. + InstanceAdminClient instanceAdminClient, DatabaseId databaseId, BackupId backupId) { + Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); + // Get create backup operations for the sample database. + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); String filter = String.format( - "(metadata.@type:type.googleapis.com/" - + "google.spanner.admin.database.v1.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); + "(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.CreateBackupMetadata) " + + "AND (metadata.database:%s)", + databaseId.getName()); + Page createBackupOperations = instance.listBackupOperations( + Options.filter(filter)); System.out.println("Create Backup Operations:"); for (Operation op : createBackupOperations.iterateAll()) { try { @@ -1679,27 +1693,24 @@ static void listBackupOperations( } } // 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); + filter = + String.format( + "(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.CopyBackupMetadata) " + + "AND (metadata.source_backup:%s)", + backupId.getName()); + Page copyBackupOperations = instance.listBackupOperations(Options.filter(filter)); System.out.println("Copy Backup Operations:"); for (Operation op : copyBackupOperations.iterateAll()) { try { CopyBackupMetadata copyBackupMetadata = - op.getMetadata().unpack(CopyBackupMetadata.class); + 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())); + 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()); @@ -1710,23 +1721,18 @@ static void listBackupOperations( // [START spanner_list_database_operations] static void listDatabaseOperations( - DatabaseAdminClient dbAdminClient, String projectId, String instanceId) { + InstanceAdminClient instanceAdminClient, + DatabaseAdminClient dbAdminClient, + InstanceId instanceId) { + Instance instance = instanceAdminClient.getInstance(instanceId.getInstance()); // 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); + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); String filter = String.format("(metadata.@type:type.googleapis.com/" - + "google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata) AND " - + "(metadata.progress.start_time > \"%s\")", last24Hours); - 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()) { + + "google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata) AND " + + "(metadata.progress.start_time > \"%s\")", last24Hours); + for (Operation op : instance.listDatabaseOperations(Options.filter(filter)).iterateAll()) { try { OptimizeRestoredDatabaseMetadata metadata = op.getMetadata().unpack(OptimizeRestoredDatabaseMetadata.class); @@ -1744,92 +1750,74 @@ static void listDatabaseOperations( // [START spanner_list_backups] static void listBackups( - DatabaseAdminClient dbAdminClient, String projectId, - String instanceId, String databaseId, String backupId) { - InstanceName instanceName = InstanceName.of(projectId, instanceId); + InstanceAdminClient instanceAdminClient, DatabaseId databaseId, BackupId backupId) { + Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); // List all backups. System.out.println("All backups:"); - for (Backup backup : dbAdminClient.listBackups( - instanceName.toString()).iterateAll()) { + for (Backup backup : instance.listBackups().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()) { + String.format("All backups with backup name containing \"%s\":", backupId.getBackup())); + for (Backup backup : instance.listBackups( + Options.filter(String.format("name:%s", backupId.getBackup()))).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()) { + "All backups for databases with a name containing \"%s\":", + databaseId.getDatabase())); + for (Backup backup : instance.listBackups( + Options.filter(String.format("database:%s", databaseId.getDatabase()))).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()) { + Timestamp expireTime = 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.toString())); + for (Backup backup : + instance.listBackups( + Options.filter(String.format("expire_time < \"%s\"", expireTime.toString()))) + .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()) { + for (Backup backup : instance.listBackups(Options.filter("size_bytes > 100")).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)); - + Timestamp createTime = 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()) { + for (Backup backup : + instance + .listBackups(Options.filter( + String.format("create_time >= \"%s\" AND state:READY", createTime.toString()))) + .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(); + Page page = instance.listBackups(Options.pageSize(10)); while (true) { - ListBackupsPagedResponse response = dbAdminClient.listBackups(listBackupsRequest); - for (Backup backup : response.getPage().iterateAll()) { + for (Backup backup : page.getValues()) { System.out.println(backup); } - String nextPageToken = response.getNextPageToken(); - if (!Strings.isNullOrEmpty(nextPageToken)) { - listBackupsRequest = listBackupsRequest.toBuilder().setPageToken(nextPageToken).build(); - } else { + if (!page.hasNextPage()) { break; } + page = page.getNextPage(); } } // [END spanner_list_backups] @@ -1837,35 +1825,31 @@ static void listBackups( // [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); + BackupId backupId, + DatabaseId sourceDatabaseId, + DatabaseId restoreToDatabase) { + Backup backup = dbAdminClient.newBackupBuilder(backupId).build(); // Initiate the request which returns an OperationFuture. System.out.println(String.format( - "Restoring backup [%s] to database [%s]...", backup.getName(), restoreToDatabaseId)); + "Restoring backup [%s] to database [%s]...", + backup.getId().toString(), + restoreToDatabase.toString())); try { - RestoreDatabaseRequest request = - RestoreDatabaseRequest.newBuilder() - .setParent(InstanceName.of(projectId, instanceId).toString()) - .setDatabaseId(restoreToDatabaseId) - .setBackup(backupName.toString()).build(); - OperationFuture op = - dbAdminClient.restoreDatabaseAsync(request); + OperationFuture op = backup.restore(restoreToDatabase); // 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(); - + Database db = op.get(); + // Refresh database metadata and get the restore info. + RestoreInfo restore = db.reload().getRestoreInfo(); + Timestamp versionTime = Timestamp.fromProto(restore + .getProto() + .getBackupInfo() + .getVersionTime()); System.out.println( "Restored database [" - + db.getName() + + restore.getSourceDatabase().getName() + "] from [" - + restoreInfo.getBackupInfo().getBackup() - + "] with version time [" + backupInfo.getVersionTime() + "]"); + + restore.getBackup().getName() + + "] with version time [" + versionTime + "]"); } catch (ExecutionException e) { throw SpannerExceptionFactory.newSpannerException(e.getCause()); } catch (InterruptedException e) { @@ -1875,59 +1859,49 @@ static void restoreBackup( // [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); - + static void updateBackup(DatabaseAdminClient dbAdminClient, BackupId backupId) { // Get current backup metadata. - Backup backup = dbAdminClient.getBackup(backupName); + Backup backup = dbAdminClient.newBackupBuilder(backupId).build().reload(); // 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()) + Timestamp expireTime = + Timestamp.ofTimeMicroseconds( + TimeUnit.SECONDS.toMicros(backup.getExpireTime().getSeconds()) + + TimeUnit.NANOSECONDS.toMicros(backup.getExpireTime().getNanos()) + TimeUnit.DAYS.toMicros(30L)); - // New Expire Time must be less than Max Expire Time - newExpireTime = - newExpireTime.compareTo(com.google.cloud.Timestamp.fromProto(backup.getMaxExpireTime())) - < 0 ? newExpireTime : com.google.cloud.Timestamp.fromProto(backup.getMaxExpireTime()); + expireTime = expireTime.compareTo(backup.getMaxExpireTime()) + < 0 ? expireTime : backup.getMaxExpireTime(); + int timeDiff = expireTime.compareTo(backup.getExpireTime()); + Timestamp newExpireTime = (timeDiff < 0) ? expireTime : backup.getExpireTime(); System.out.println(String.format( "Updating expire time of backup [%s] to %s...", backupId.toString(), - java.time.OffsetDateTime.ofInstant( - Instant.ofEpochSecond(newExpireTime.getSeconds(), - newExpireTime.getNanos()), ZoneId.systemDefault()))); + LocalDateTime.ofEpochSecond( + expireTime.getSeconds(), + expireTime.getNanos(), + OffsetDateTime.now().getOffset()).toString())); // Update expire time. - backup = backup.toBuilder().setExpireTime(newExpireTime.toProto()).build(); - dbAdminClient.updateBackup(backup, - FieldMask.newBuilder().addAllPaths(Lists.newArrayList("expire_time")).build()); + backup = backup.toBuilder().setExpireTime(expireTime).build(); + backup.updateExpireTime(); 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); - + static void deleteBackup(DatabaseAdminClient dbAdminClient, BackupId backupId) { + Backup backup = dbAdminClient.newBackupBuilder(backupId).build(); // Delete the backup. System.out.println("Deleting backup [" + backupId + "]..."); - dbAdminClient.deleteBackup(backupName); + backup.delete(); // 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); - } + if (backup.exists()) { + System.out.println("Delete backup [" + backupId + "] failed"); + throw new RuntimeException("Delete backup [" + backupId + "] failed"); + } else { + System.out.println("Deleted backup [" + backupId + "]"); } } // [END spanner_delete_backup] @@ -1935,13 +1909,13 @@ static void deleteBackup(DatabaseAdminClient dbAdminClient, static void run( DatabaseClient dbClient, DatabaseAdminClient dbAdminClient, + InstanceAdminClient instanceAdminClient, String command, DatabaseId database, - String backupId) { + BackupId backup) { switch (command) { case "createdatabase": - createDatabase(dbAdminClient, InstanceName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance()), database.getDatabase()); + createDatabase(dbAdminClient, database); break; case "write": writeExampleData(dbClient); @@ -1956,8 +1930,7 @@ static void run( read(dbClient); break; case "addmarketingbudget": - addMarketingBudget(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase())); + addMarketingBudget(dbAdminClient, database); break; case "update": update(dbClient); @@ -1969,8 +1942,7 @@ static void run( queryMarketingBudget(dbClient); break; case "addindex": - addIndex(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase())); + addIndex(dbAdminClient, database); break; case "readindex": readUsingIndex(dbClient); @@ -1979,8 +1951,7 @@ static void run( queryUsingIndex(dbClient); break; case "addstoringindex": - addStoringIndex(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase())); + addStoringIndex(dbAdminClient, database); break; case "readstoringindex": readStoringIndex(dbClient); @@ -1992,8 +1963,7 @@ static void run( readStaleData(dbClient); break; case "addcommittimestamp": - addCommitTimestamp(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase())); + addCommitTimestamp(dbAdminClient, database); break; case "updatewithtimestamp": updateWithTimestamp(dbClient); @@ -2002,9 +1972,7 @@ static void run( queryMarketingBudgetWithTimestamp(dbClient); break; case "createtablewithtimestamp": - createTableWithTimestamp(dbAdminClient, - DatabaseName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase())); + createTableWithTimestamp(dbAdminClient, database); break; case "writewithtimestamp": writeExampleDataWithTimestamp(dbClient); @@ -2067,9 +2035,7 @@ static void run( updateUsingBatchDml(dbClient); break; case "createtablewithdatatypes": - createTableWithDatatypes(dbAdminClient, - DatabaseName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase())); + createTableWithDatatypes(dbAdminClient, database); break; case "writedatatypesdata": writeDatatypesData(dbClient); @@ -2108,41 +2074,35 @@ static void run( queryWithQueryOptions(dbClient); break; case "createbackup": - createBackup(dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase(), - backupId, getVersionTime(dbClient)); + createBackup(dbAdminClient, database, backup, getVersionTime(dbClient)); break; case "cancelcreatebackup": cancelCreateBackup( dbAdminClient, - database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase(), - backupId + "_cancel"); + database, + BackupId.of(backup.getInstanceId(), backup.getBackup() + "_cancel")); break; case "listbackupoperations": - listBackupOperations(dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase(), backupId); + listBackupOperations(instanceAdminClient, database, backup); break; case "listdatabaseoperations": - listDatabaseOperations(dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance()); + listDatabaseOperations(instanceAdminClient, dbAdminClient, database.getInstanceId()); break; case "listbackups": - listBackups(dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase(), backupId); + listBackups(instanceAdminClient, database, backup); break; case "restorebackup": restoreBackup( - dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), backupId, database.getDatabase()); + dbAdminClient, + backup, + database, + DatabaseId.of(database.getInstanceId(), createRestoredSampleDbId(database))); break; case "updatebackup": - updateBackup(dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), backupId); + updateBackup(dbAdminClient, backup); break; case "deletebackup": - deleteBackup(dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), backupId); + deleteBackup(dbAdminClient, backup); break; default: printUsageAndExit(); @@ -2151,13 +2111,13 @@ static void run( static Timestamp getVersionTime(DatabaseClient dbClient) { // Generates a version time for the backup - com.google.cloud.Timestamp versionTime; + Timestamp versionTime; try (ResultSet resultSet = dbClient.singleUse() .executeQuery(Statement.of("SELECT CURRENT_TIMESTAMP()"))) { resultSet.next(); versionTime = resultSet.getTimestamp(0); } - return versionTime.toProto(); + return versionTime; } static void printUsageAndExit() { @@ -2225,16 +2185,15 @@ static void printUsageAndExit() { System.exit(1); } - public static void main(String[] args) { + 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]; + 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. @@ -2247,26 +2206,25 @@ public static void main(String[] args) { printUsageAndExit(); } // Generate a backup id for the sample database. - String backupId = null; + String backupName = + String.format( + "%s_%02d", + db.getDatabase(), LocalDate.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR)); + BackupId backup = BackupId.of(db.getInstanceId(), backupName); if (args.length == 4) { - backupId = args[3]; + backupName = args[3]; } // [START init_client] DatabaseClient dbClient = spanner.getDatabaseClient(db); - dbAdminClient = spanner.createDatabaseAdminClient(); - + DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); // Use client here... // [END init_client] - run(dbClient, dbAdminClient, command, db, backupId); + run(dbClient, dbAdminClient, instanceAdminClient, command, db, backup); // [START init_client] } finally { - if (dbAdminClient != null) { - if (!dbAdminClient.isShutdown() || !dbAdminClient.isTerminated()) { - dbAdminClient.close(); - } - } spanner.close(); } // [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/archived/UpdateDatabaseSample.java similarity index 62% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/UpdateDatabaseSample.java index 4e4f76d700..5fcd9ab5ed 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/UpdateDatabaseSample.java @@ -14,21 +14,18 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_update_database] - import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.DatabaseInfo.DatabaseField; 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; -import com.google.spanner.admin.database.v1.Database; -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.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -40,31 +37,22 @@ static void updateDatabase() { final String projectId = "my-project"; final String instanceId = "my-instance"; final String databaseId = "my-database"; - updateDatabase(projectId, instanceId, databaseId); } - static void updateDatabase( - String projectId, String instanceId, String databaseId) { + static void updateDatabase(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()) - .setEnableDropProtection(true).build(); - final UpdateDatabaseRequest updateDatabaseRequest = - UpdateDatabaseRequest.newBuilder() - .setDatabase(database) - .setUpdateMask( - FieldMask.newBuilder().addAllPaths( - Lists.newArrayList("enable_drop_protection")).build()) - .build(); + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + + DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId); + Database databaseToUpdate = + databaseAdminClient.newDatabaseBuilder(dbId).enableDropProtection().build(); OperationFuture operation = - databaseAdminClient.updateDatabaseAsync(updateDatabaseRequest); - System.out.printf("Waiting for update operation for %s to complete...\n", databaseId); + databaseAdminClient.updateDatabase(databaseToUpdate, DatabaseField.DROP_PROTECTION); + System.out.printf("Waiting for update operation for %s to complete...\n", dbId); Database updatedDb = operation.get(5, TimeUnit.MINUTES); - System.out.printf("Updated database %s.\n", updatedDb.getName()); + System.out.printf("Updated database %s.\n", updatedDb.getId().getName()); } catch (ExecutionException | TimeoutException 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/UpdateDatabaseWithDefaultLeaderSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/archived/UpdateDatabaseWithDefaultLeaderSample.java similarity index 76% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/UpdateDatabaseWithDefaultLeaderSample.java index 26c252ac1e..8971399093 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/UpdateDatabaseWithDefaultLeaderSample.java @@ -14,16 +14,17 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; //[START spanner_update_database_with_default_leader] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; -import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient; -import com.google.spanner.admin.database.v1.DatabaseName; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import java.util.Collections; import java.util.concurrent.ExecutionException; @@ -40,20 +41,26 @@ static void updateDatabaseWithDefaultLeader() { static void updateDatabaseWithDefaultLeader( 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), + try (Spanner spanner = SpannerOptions + .newBuilder() + .setProjectId(projectId) + .build() + .getService()) { + final DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + final OperationFuture operation = databaseAdminClient + .updateDatabaseDdl( + instanceId, + databaseId, Collections.singletonList( String.format( "ALTER DATABASE `%s` SET OPTIONS (default_leader = '%s')", databaseId, defaultLeader ) - ) - ).get(); + ), + null + ); + operation.get(); System.out.println("Updated default leader to " + defaultLeader); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. 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/archived/UpdateInstanceConfigSample.java similarity index 52% rename from samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateInstanceConfigSample.java rename to samples/snippets/src/main/java/com/example/spanner/admin/archived/UpdateInstanceConfigSample.java index e74d8071c2..8110e3782c 100644 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/UpdateInstanceConfigSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/admin/archived/UpdateInstanceConfigSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,24 +14,24 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; // [START spanner_update_instance_config] - +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceConfig; +import com.google.cloud.spanner.InstanceConfigId; +import com.google.cloud.spanner.InstanceConfigInfo; +import com.google.cloud.spanner.InstanceConfigInfo.InstanceConfigField; 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 com.google.spanner.admin.instance.v1.UpdateInstanceConfigMetadata; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; class UpdateInstanceConfigSample { - static void updateInstanceConfig() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; @@ -41,42 +41,27 @@ static void updateInstanceConfig() { 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 = - InstanceConfig.newBuilder() - .setName(instanceConfigName.toString()) + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + final InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + InstanceConfigInfo instanceConfigInfo = + InstanceConfig.newBuilder(InstanceConfigId.of(projectId, instanceConfigId)) .setDisplayName("updated custom instance config") - .putLabels("updated", "true").build(); - /** - * The field mask must always be specified; this prevents any future - * fields in [InstanceConfig][google.spanner.admin.instance.v1.InstanceConfig] - * from being erased accidentally by clients that do not know about them. - */ - final UpdateInstanceConfigRequest updateInstanceConfigRequest = - UpdateInstanceConfigRequest.newBuilder() - .setInstanceConfig(instanceConfig) - .setUpdateMask( - FieldMask.newBuilder().addAllPaths(ImmutableList.of("display_name", "labels")) - .build()).build(); + .addLabel("updated", "true") + .build(); + final OperationFuture operation = + instanceAdminClient.updateInstanceConfig( + instanceConfigInfo, + ImmutableList.of(InstanceConfigField.DISPLAY_NAME, InstanceConfigField.LABELS)); try { - System.out.printf("Waiting for update operation on %s to complete...\n", - instanceConfigName); - InstanceConfig instanceConfigResult = - instanceAdminClient.updateInstanceConfigAsync( - updateInstanceConfigRequest).get(5, TimeUnit.MINUTES); + System.out.printf("Waiting for update operation on %s to complete...\n", instanceConfigId); + InstanceConfig instanceConfig = operation.get(5, TimeUnit.MINUTES); System.out.printf( "Updated instance configuration %s with new display name %s\n", - instanceConfigResult.getName(), instanceConfig.getDisplayName()); + instanceConfig.getId(), instanceConfig.getDisplayName()); } catch (ExecutionException | TimeoutException e) { System.out.printf( "Error: Updating instance config %s failed with error message %s\n", - instanceConfig.getName(), e.getMessage()); + instanceConfigInfo.getId(), e.getMessage()); e.printStackTrace(); } catch (InterruptedException e) { System.out.println( 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 deleted file mode 100644 index 69a154e882..0000000000 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/AddAndDropDatabaseRole.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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_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.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -public class AddAndDropDatabaseRole { - - static void addAndDropDatabaseRole() { - // TODO(developer): Replace these variables before running the sample. - String projectId = "my-project"; - String instanceId = "my-instance"; - String databaseId = "my-database"; - String parentRole = "parent_role"; - String childRole = "child_role"; - addAndDropDatabaseRole(projectId, instanceId, databaseId, parentRole, childRole); - } - - static void addAndDropDatabaseRole( - 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), - ImmutableList.of( - String.format("CREATE ROLE %s", parentRole), - String.format("GRANT SELECT ON TABLE Albums TO ROLE %s", parentRole), - String.format("CREATE ROLE %s", childRole), - String.format("GRANT ROLE %s TO ROLE %s", parentRole, childRole))) - .get(5, TimeUnit.MINUTES); - System.out.printf( - "Created roles %s and %s and granted privileges%n", parentRole, childRole); - // Delete role and membership. - System.out.println("Waiting for role revoke & drop operation to complete..."); - databaseAdminClient.updateDatabaseDdlAsync( - DatabaseName.of(projectId, instanceId, databaseId), - ImmutableList.of( - String.format("REVOKE ROLE %s FROM ROLE %s", parentRole, childRole), - String.format("DROP ROLE %s", childRole))).get(5, TimeUnit.MINUTES); - System.out.printf("Revoked privileges and dropped role %s%n", childRole); - } catch (ExecutionException | TimeoutException e) { - System.out.printf( - "Error: AddAndDropDatabaseRole failed with error message %s\n", e.getMessage()); - e.printStackTrace(); - } catch (InterruptedException e) { - System.out.println( - "Error: Waiting for AddAndDropDatabaseRole operation to finish was interrupted"); - } - } -} -// [END spanner_add_and_drop_database_role] 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 deleted file mode 100644 index 245abd9429..0000000000 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CopyBackupSample.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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.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.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() { - // TODO(developer): Replace these variables before running the sample. - String projectId = "my-project"; - String instanceId = "my-instance"; - String sourceBackupId = "my-backup"; - String destinationBackupId = "my-destination-backup"; - - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService(); - DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) { - 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/CreateInstanceConfigSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceConfigSample.java deleted file mode 100644 index 8671c6eda3..0000000000 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceConfigSample.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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; - -// [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.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -class CreateInstanceConfigSample { - - static void createInstanceConfig() { - // TODO(developer): Replace these variables before running the sample. - String projectId = "my-project"; - String baseInstanceConfigId = "nam11"; - String instanceConfigId = "custom-instance-config4"; - - createInstanceConfig(projectId, baseInstanceConfigId, instanceConfigId); - } - - static void createInstanceConfig( - 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 = - instanceAdminClient.getInstanceConfig(baseInstanceConfigName.toString()); - final InstanceConfigName instanceConfigName = InstanceConfigName.of(projectId, - instanceConfigId); - /** - * The replicas for the custom instance configuration must include all the replicas of the - * base configuration, in addition to at least one from the list of optional replicas of the - * base configuration. - */ - final List replicas = - Stream.concat(baseConfig.getReplicasList().stream(), - baseConfig.getOptionalReplicasList().stream().limit(1)).collect(Collectors.toList()); - final InstanceConfig instanceConfig = - InstanceConfig.newBuilder().setName(instanceConfigName.toString()) - .setBaseConfig(baseInstanceConfigName.toString()) - .setDisplayName("Instance Configuration").addAllReplicas(replicas).build(); - final CreateInstanceConfigRequest createInstanceConfigRequest = - CreateInstanceConfigRequest.newBuilder().setParent(ProjectName.of(projectId).toString()) - .setInstanceConfigId(instanceConfigId).setInstanceConfig(instanceConfig).build(); - try { - System.out.printf("Waiting for create operation for %s to complete...\n", - instanceConfigName); - InstanceConfig instanceConfigResult = - instanceAdminClient.createInstanceConfigAsync( - createInstanceConfigRequest).get(5, TimeUnit.MINUTES); - System.out.printf("Created instance configuration %s\n", instanceConfigResult.getName()); - } catch (ExecutionException | TimeoutException e) { - System.out.printf( - "Error: Creating instance configuration %s failed with error message %s\n", - instanceConfig.getName(), e.getMessage()); - } catch (InterruptedException e) { - System.out.println( - "Error: Waiting for createInstanceConfig operation to finish was interrupted"); - } - } - } -} -// [END spanner_create_instance_config] 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 deleted file mode 100644 index 3faaced75b..0000000000 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigExample.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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; - -// [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.util.concurrent.ExecutionException; - -class CreateInstanceWithAutoscalingConfigExample { - - 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) { - 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"; - - // Create an autoscaling config. - // When autoscaling_config is enabled, node_count and processing_units fields - // need not be specified. - AutoscalingConfig autoscalingConfig = - AutoscalingConfig.newBuilder() - .setAutoscalingLimits( - AutoscalingConfig.AutoscalingLimits.newBuilder().setMinNodes(1).setMaxNodes(2)) - .setAutoscalingTargets( - AutoscalingConfig.AutoscalingTargets.newBuilder() - .setHighPriorityCpuUtilizationPercent(65) - .setStorageUtilizationPercent(95)) - .build(); - Instance instance = - Instance.newBuilder() - .setAutoscalingConfig(autoscalingConfig) - .setDisplayName(displayName) - .setConfig( - InstanceConfigName.of(projectId, configId).toString()) - .build(); - - // Creates a new instance - System.out.printf("Creating instance %s.%n", instanceId); - try { - // Wait for the createInstance operation to finish. - Instance instanceResult = instanceAdminClient.createInstanceAsync( - CreateInstanceRequest.newBuilder() - .setParent(ProjectName.of(projectId).toString()) - .setInstanceId(instanceId) - .setInstance(instance) - .build()).get(); - System.out.printf("Autoscaler instance %s was successfully created%n", - instanceResult.getName()); - } catch (ExecutionException e) { - System.out.printf( - "Error: Creating instance %s failed with error message %s%n", - instance.getName(), e.getMessage()); - } catch (InterruptedException e) { - System.out.println("Error: Waiting for createInstance operation to finish was interrupted"); - } - } - } -} -// [END spanner_create_instance_with_autoscaling_config] 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 deleted file mode 100644 index 2f6b6d9887..0000000000 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsExample.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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; - -//[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; - -class CreateInstanceWithProcessingUnitsExample { - - 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) { - 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. - int processingUnits = 500; - String displayName = "Descriptive name"; - - try { - // Creates a new instance - System.out.printf("Creating instance %s.%n", instanceId); - Instance instance = - Instance.newBuilder() - .setDisplayName(displayName) - .setProcessingUnits(processingUnits) - .setConfig( - InstanceConfigName.of(projectId, configId).toString()) - .build(); - // Wait for the createInstance operation to finish. - System.out.printf("Waiting for operation on %s to complete...%n", instanceId); - Instance createdInstance = instanceAdminClient.createInstanceAsync( - CreateInstanceRequest.newBuilder() - .setParent(ProjectName.of(projectId).toString()) - .setInstanceId(instanceId) - .setInstance(instance) - .build()).get(); - - System.out.printf("Created instance %s.%n", createdInstance.getName()); - System.out.printf("Instance %s has %d processing units.%n", createdInstance.getName(), - createdInstance.getProcessingUnits()); - } catch (Exception e) { - System.out.printf("Error: %s.%n", e.getMessage()); - } - } - } -} -//[END spanner_create_instance_with_processing_units] 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 deleted file mode 100644 index 84d09792c1..0000000000 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateTableWithForeignKeyDeleteCascadeSample.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.spanner.admin.generated; - -// [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.DatabaseName; - -class CreateTableWithForeignKeyDeleteCascadeSample { - - static void createForeignKeyDeleteCascadeConstraint() { - // TODO(developer): Replace these variables before running the sample. - String projectId = "my-project"; - String instanceId = "my-instance"; - String databaseId = "my-database"; - - createForeignKeyDeleteCascadeConstraint(projectId, instanceId, databaseId); - } - - static void createForeignKeyDeleteCascadeConstraint( - 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/ListInstanceConfigOperationsSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigOperationsSample.java deleted file mode 100644 index cbfd1908d4..0000000000 --- a/samples/snippets/src/main/java/com/example/spanner/admin/generated/ListInstanceConfigOperationsSample.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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; - -// [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; - -public class ListInstanceConfigOperationsSample { - - static void listInstanceConfigOperations() { - // TODO(developer): Replace these variables before running the sample. - String projectId = "my-project"; - listInstanceConfigOperations(projectId); - } - - 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", - projectId); - final ListInstanceConfigOperationsRequest request = - ListInstanceConfigOperationsRequest.newBuilder() - .setParent(projectName.toString()) - .setFilter("(metadata.@type=type.googleapis.com/" - + "google.spanner.admin.instance.v1.CreateInstanceConfigMetadata)").build(); - final Iterable instanceConfigOperations = - instanceAdminClient.listInstanceConfigOperations(request).iterateAll(); - for (Operation operation : instanceConfigOperations) { - CreateInstanceConfigMetadata metadata = - operation.getMetadata().unpack(CreateInstanceConfigMetadata.class); - System.out.printf( - "Create instance config operation for %s is %d%% completed.\n", - metadata.getInstanceConfig().getName(), metadata.getProgress().getProgressPercent()); - } - System.out.printf( - "Obtained list of instance config operations for project %s...\n", - projectName); - } catch (InvalidProtocolBufferException e) { - System.out.printf( - "Error: Listing instance config operations failed with error message %s\n", - e.getMessage()); - } - } -} -// [END spanner_list_instance_config_operations] diff --git a/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java index f719424758..a22b5ab375 100644 --- a/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java @@ -18,22 +18,24 @@ import static org.junit.Assert.assertTrue; +import com.google.spanner.admin.database.v1.CreateDatabaseRequest; +import com.google.spanner.admin.database.v1.InstanceName; import java.util.Arrays; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class AlterTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBase { +public class AlterTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBaseV2 { @Test public void testAlterTableWithForeignKeyDeleteCascade() throws Exception { // Creates database final String databaseId = idGenerator.generateDatabaseId(); - databaseAdminClient - .createDatabase( - instanceId, - databaseId, - Arrays.asList( + final CreateDatabaseRequest request = + CreateDatabaseRequest.newBuilder() + .setCreateStatement("CREATE DATABASE `" + databaseId + "`") + .setParent(InstanceName.of(projectId, instanceId).toString()) + .addAllExtraStatements(Arrays.asList( "CREATE TABLE Customers (\n" + " CustomerId INT64 NOT NULL,\n" + " CustomerName STRING(62) NOT NULL,\n" @@ -45,15 +47,15 @@ public void testAlterTableWithForeignKeyDeleteCascade() throws Exception { + " CONSTRAINT FKShoppingCartsCustomerId" + " FOREIGN KEY (CustomerId)\n" + " REFERENCES Customers (CustomerId)\n" - + " ) PRIMARY KEY (CartId)\n")) - .get(5, TimeUnit.MINUTES); + + " ) PRIMARY KEY (CartId)\n")).build(); + databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample( () -> AlterTableWithForeignKeyDeleteCascadeSample.alterForeignKeyDeleteCascadeConstraint( - databaseAdminClient, instanceId, databaseId)); + projectId, instanceId, databaseId)); assertTrue( "Expected to have created database " diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CopyBackupIT.java b/samples/snippets/src/test/java/com/example/spanner/CopyBackupIT.java similarity index 98% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/CopyBackupIT.java rename to samples/snippets/src/test/java/com/example/spanner/CopyBackupIT.java index 1d51042888..29854011e1 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CopyBackupIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/CopyBackupIT.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner; 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; diff --git a/samples/snippets/src/test/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSampleIT.java index d56d4b14f8..fff67b38b8 100644 --- a/samples/snippets/src/test/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/CreateDatabaseWithDefaultLeaderSampleIT.java @@ -1,11 +1,11 @@ /* - * Copyright 2021 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -18,33 +18,34 @@ import static org.junit.Assert.assertTrue; -import com.google.cloud.spanner.InstanceConfig; -import com.google.cloud.spanner.InstanceConfigId; +import com.google.spanner.admin.instance.v1.InstanceConfig; +import com.google.spanner.admin.instance.v1.InstanceName; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; -public class CreateDatabaseWithDefaultLeaderSampleIT extends SampleTestBase { +@RunWith(JUnit4.class) +public class CreateDatabaseWithDefaultLeaderSampleIT extends SampleTestBaseV2 { @Test public void testCreateDatabaseWithDefaultLeader() throws Exception { final String databaseId = idGenerator.generateDatabaseId(); // Finds possible default leader - final InstanceConfigId instanceConfigId = instanceAdminClient - .getInstance(multiRegionalInstanceId) - .getInstanceConfigId(); - final InstanceConfig config = instanceAdminClient - .getInstanceConfig(instanceConfigId.getInstanceConfig()); + + final String instanceConfigId = instanceAdminClient.getInstance( + InstanceName.of(projectId, multiRegionalInstanceId)).getConfig(); + final InstanceConfig config = instanceAdminClient.getInstanceConfig(instanceConfigId); assertTrue( "Expected instance config " + instanceConfigId + " to have at least one leader option", - config.getLeaderOptions().size() > 0 + config.getLeaderOptionsCount() > 0 ); - final String defaultLeader = config.getLeaderOptions().get(0); + final String defaultLeader = config.getLeaderOptions(0); // Runs sample final String out = SampleRunner.runSample(() -> CreateDatabaseWithDefaultLeaderSample.createDatabaseWithDefaultLeader( - projectId, - multiRegionalInstanceId, + getInstanceName(projectId, multiRegionalInstanceId), databaseId, defaultLeader ) diff --git a/samples/snippets/src/test/java/com/example/spanner/CreateDatabaseWithVersionRetentionPeriodSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/CreateDatabaseWithVersionRetentionPeriodSampleIT.java index 0e77ffc9a2..98832f1f51 100644 --- a/samples/snippets/src/test/java/com/example/spanner/CreateDatabaseWithVersionRetentionPeriodSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/CreateDatabaseWithVersionRetentionPeriodSampleIT.java @@ -23,10 +23,11 @@ import org.junit.runners.JUnit4; /** - * Integration tests for {@link CreateDatabaseWithVersionRetentionPeriodSample} + * Integration tests for + * {@link com.example.spanner.admin.archived.CreateDatabaseWithVersionRetentionPeriodSample} */ @RunWith(JUnit4.class) -public class CreateDatabaseWithVersionRetentionPeriodSampleIT extends SampleTestBase { +public class CreateDatabaseWithVersionRetentionPeriodSampleIT extends SampleTestBaseV2 { @Test public void createsDatabaseWithVersionRetentionPeriod() throws Exception { @@ -35,7 +36,7 @@ public void createsDatabaseWithVersionRetentionPeriod() throws Exception { final String out = SampleRunner.runSample(() -> CreateDatabaseWithVersionRetentionPeriodSample .createDatabaseWithVersionRetentionPeriod( - databaseAdminClient, instanceId, databaseId, versionRetentionPeriod + projectId, instanceId, databaseId, versionRetentionPeriod )); assertThat(out).contains( diff --git a/samples/snippets/src/test/java/com/example/spanner/CreateInstanceWithAutoscalingConfigSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/CreateInstanceWithAutoscalingConfigSampleIT.java index 76eef2529d..041d6b97ac 100644 --- a/samples/snippets/src/test/java/com/example/spanner/CreateInstanceWithAutoscalingConfigSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/CreateInstanceWithAutoscalingConfigSampleIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Google LLC + * 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. @@ -18,25 +18,19 @@ import static com.google.common.truth.Truth.assertThat; -import com.google.cloud.spanner.InstanceId; -import java.util.UUID; +import com.google.spanner.admin.database.v1.InstanceName; import org.junit.Test; -public class CreateInstanceWithAutoscalingConfigSampleIT extends SampleTestBase { +public class CreateInstanceWithAutoscalingConfigSampleIT extends SampleTestBaseV2 { @Test public void testCreateInstanceWithAutoscalingConfig() throws Exception { - String instanceId = String.format("autoscaler-%s", UUID.randomUUID()); + String instanceId = idGenerator.generateInstanceId(); String out = SampleRunner.runSample( - () -> { - try { - CreateInstanceWithAutoscalingConfigExample.createInstance(projectId, instanceId); - } finally { - spanner.getInstanceAdminClient().deleteInstance(instanceId); - } - }); + () -> CreateInstanceWithAutoscalingConfigExample.createInstance(projectId, instanceId)); assertThat(out) - .contains(String.format("Autoscaler instance %s", InstanceId.of(projectId, instanceId))); + .contains(String.format("Autoscaler instance %s", + InstanceName.of(projectId, instanceId).toString())); } } diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/CreateInstanceWithProcessingUnitsSampleIT.java similarity index 93% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/CreateInstanceWithProcessingUnitsSampleIT.java index 13778dff82..dcddea4929 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateInstanceWithProcessingUnitsSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/CreateInstanceWithProcessingUnitsSampleIT.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner; import static com.google.common.truth.Truth.assertThat; -import com.example.spanner.SampleRunner; import com.google.spanner.admin.database.v1.InstanceName; import org.junit.Test; diff --git a/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java index b2dd6638ce..481bdc16bd 100644 --- a/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java @@ -18,28 +18,30 @@ import static org.junit.Assert.assertTrue; -import java.util.Arrays; +import com.google.spanner.admin.database.v1.CreateDatabaseRequest; +import com.google.spanner.admin.database.v1.InstanceName; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class CreateTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBase { +public class CreateTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBaseV2 { @Test public void testCreateTableWithForeignKeyDeleteCascade() throws Exception { // Creates database final String databaseId = idGenerator.generateDatabaseId(); - databaseAdminClient - .createDatabase(instanceId, databaseId, Arrays.asList()) - .get(5, TimeUnit.MINUTES); + final CreateDatabaseRequest request = + CreateDatabaseRequest.newBuilder() + .setCreateStatement("CREATE DATABASE `" + databaseId + "`") + .setParent(InstanceName.of(projectId, instanceId).toString()).build(); + databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample( () -> CreateTableWithForeignKeyDeleteCascadeSample - .createForeignKeyDeleteCascadeConstraint( - databaseAdminClient, instanceId, databaseId)); + .createForeignKeyDeleteCascadeConstraint(projectId, instanceId, databaseId)); assertTrue( "Expected to have created database " diff --git a/samples/snippets/src/test/java/com/example/spanner/CustomInstanceConfigSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/CustomInstanceConfigSampleIT.java index ba1eef6850..354e11a504 100644 --- a/samples/snippets/src/test/java/com/example/spanner/CustomInstanceConfigSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/CustomInstanceConfigSampleIT.java @@ -1,11 +1,11 @@ /* - * Copyright 2022 Google LLC + * 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 + * 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, @@ -19,8 +19,11 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; -public class CustomInstanceConfigSampleIT extends SampleTestBase { +@RunWith(JUnit4.class) +public class CustomInstanceConfigSampleIT extends SampleTestBaseV2 { @Test public void testCustomInstanceConfigOperations() throws Exception { @@ -34,26 +37,37 @@ public void testCustomInstanceConfigOperations() throws Exception { projectId, instanceConfigName, customInstanceConfigId)); assertTrue(out1.contains("Created instance configuration")); - // List the instance config operations. + // Fetch the instance config that was created above. final String out2 = + SampleRunner.runSample( + () -> GetInstanceConfigSample.getInstanceConfig(projectId, instanceConfigName)); + assertTrue(out2.contains("Available leader options for instance config")); + + // Fetch the instance config that was created above. + final String out3 = + SampleRunner.runSample( + () -> ListInstanceConfigsSample.listInstanceConfigs(projectId)); + assertTrue(out3.contains("Available leader options for instance config")); + + // List the instance config operations. + final String out4 = SampleRunner.runSample( () -> ListInstanceConfigOperationsSample.listInstanceConfigOperations(projectId)); - assertTrue(out2.contains("List instance config operation")); + assertTrue(out4.contains("Obtained list of instance config operations")); // Update display name to a randomly generated instance config id. - final String out3 = + final String out5 = SampleRunner.runSample( () -> - UpdateInstanceConfigSample.updateInstanceConfig( - projectId, customInstanceConfigId)); - assertTrue(out3.contains("Updated instance configuration")); + UpdateInstanceConfigSample.updateInstanceConfig(projectId, customInstanceConfigId)); + assertTrue(out5.contains("Updated instance configuration")); // Delete the created instance config. - final String out4 = + final String out6 = SampleRunner.runSample( () -> DeleteInstanceConfigSample.deleteInstanceConfig(projectId, customInstanceConfigId)); - assertTrue(out4.contains("Deleted instance configuration")); + assertTrue(out6.contains("Deleted instance configuration")); } } diff --git a/samples/snippets/src/test/java/com/example/spanner/DatabaseRolesIT.java b/samples/snippets/src/test/java/com/example/spanner/DatabaseRolesIT.java index b0a506430a..6c2b41c367 100644 --- a/samples/snippets/src/test/java/com/example/spanner/DatabaseRolesIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/DatabaseRolesIT.java @@ -22,7 +22,8 @@ import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.spanner.admin.database.v1.CreateDatabaseRequest; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.TimeUnit; @@ -33,20 +34,24 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Integration tests for FGAC samples for GoogleStandardSql dialect. */ +/** + * Integration tests for FGAC samples for GoogleStandardSql dialect. + */ @RunWith(JUnit4.class) -public class DatabaseRolesIT extends SampleTestBase { +public class DatabaseRolesIT extends SampleTestBaseV2 { private static DatabaseId databaseId; @BeforeClass public static void createTestDatabase() throws Exception { final String database = idGenerator.generateDatabaseId(); - databaseAdminClient - .createDatabase( - instanceId, - database, - ImmutableList.of( + final CreateDatabaseRequest request = + CreateDatabaseRequest.newBuilder() + .setParent( + com.google.spanner.admin.database.v1.InstanceName.of(projectId, instanceId) + .toString()) + .setCreateStatement("CREATE DATABASE `" + database + "`") + .addAllExtraStatements(Lists.newArrayList( "CREATE TABLE Singers (" + " SingerId INT64 NOT NULL," + " FirstName STRING(1024)," @@ -61,8 +66,8 @@ public static void createTestDatabase() throws Exception { + " AlbumTitle STRING(MAX)," + " MarketingBudget INT64" + ") PRIMARY KEY (SingerId, AlbumId)," - + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")) - .get(10, TimeUnit.MINUTES); + + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")).build(); + databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); databaseId = DatabaseId.of(projectId, instanceId, database); } @@ -103,7 +108,7 @@ public void testAddAndDropDatabaseRole() throws Exception { SampleRunner.runSample( () -> AddAndDropDatabaseRole.addAndDropDatabaseRole( - projectId, instanceId, databaseId.getDatabase(), "new-parent", "new-child")); + projectId, instanceId, databaseId.getDatabase(), "new_parent", "new_child")); assertTrue(out.contains("Created roles new_parent and new_child and granted privileges")); assertTrue(out.contains("Revoked privileges and dropped role new_child")); } @@ -115,7 +120,7 @@ public void testListDatabaseRoles() throws Exception { () -> ListDatabaseRoles.listDatabaseRoles( projectId, instanceId, databaseId.getDatabase())); - assertTrue(out.contains("new_parent")); + assertTrue(out.contains("Obtained role ")); } @Test diff --git a/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java index 1c58daded1..2e763949f4 100644 --- a/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java @@ -18,22 +18,24 @@ import static org.junit.Assert.assertTrue; -import java.util.Arrays; +import com.google.common.collect.Lists; +import com.google.spanner.admin.database.v1.CreateDatabaseRequest; +import com.google.spanner.admin.database.v1.InstanceName; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class DropForeignKeyConstraintDeleteCascadeSampleIT extends SampleTestBase { +public class DropForeignKeyConstraintDeleteCascadeSampleIT extends SampleTestBaseV2 { @Test public void testDropForeignKeyConstraintDeleteCascade() throws Exception { // Creates database final String databaseId = idGenerator.generateDatabaseId(); - databaseAdminClient - .createDatabase( - instanceId, - databaseId, - Arrays.asList( + final CreateDatabaseRequest request = + CreateDatabaseRequest.newBuilder() + .setCreateStatement("CREATE DATABASE `" + databaseId + "`") + .setParent(InstanceName.of(projectId, instanceId).toString()) + .addAllExtraStatements(Lists.newArrayList( "CREATE TABLE Customers (\n" + " CustomerId INT64 NOT NULL,\n" + " CustomerName STRING(62) NOT NULL,\n" @@ -45,15 +47,15 @@ public void testDropForeignKeyConstraintDeleteCascade() throws Exception { + " CONSTRAINT FKShoppingCartsCustomerName" + " FOREIGN KEY (CustomerName)\n" + " REFERENCES Customers (CustomerName) ON DELETE CASCADE\n" - + " ) PRIMARY KEY (CartId)\n")) - .get(5, TimeUnit.MINUTES); + + " ) PRIMARY KEY (CartId)\n")).build(); + databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample( () -> DropForeignKeyConstraintDeleteCascadeSample.deleteForeignKeyDeleteCascadeConstraint( - databaseAdminClient, instanceId, databaseId)); + projectId, instanceId, databaseId)); assertTrue( "Expected to have dropped foreign-key constraints from tables in created database " diff --git a/samples/snippets/src/test/java/com/example/spanner/EncryptionKeyIT.java b/samples/snippets/src/test/java/com/example/spanner/EncryptionKeyIT.java index 03aa10a55f..2470540914 100644 --- a/samples/snippets/src/test/java/com/example/spanner/EncryptionKeyIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/EncryptionKeyIT.java @@ -18,11 +18,14 @@ import static com.google.common.truth.Truth.assertThat; +import com.example.spanner.admin.archived.CreateDatabaseWithEncryptionKey; 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; @@ -32,12 +35,13 @@ import org.junit.runners.JUnit4; /** - * Integration tests for: {@link CreateDatabaseWithEncryptionKey}, {@link - * CreateBackupWithEncryptionKey} and {@link RestoreBackupWithEncryptionKey} + * Integration tests for: {@link CreateDatabaseWithEncryptionKey}, + * {@link com.example.spanner.admin.archived.CreateBackupWithEncryptionKey} and + * {@link com.example.spanner.admin.archived.RestoreBackupWithEncryptionKey} */ @RunWith(JUnit4.class) @Ignore -public class EncryptionKeyIT extends SampleTestBase { +public class EncryptionKeyIT extends SampleTestBaseV2 { private static String key; @@ -58,16 +62,10 @@ public void testEncryptedDatabaseAndBackupAndRestore() throws Exception { final String restoreId = idGenerator.generateDatabaseId(); String out = SampleRunner.runSample(() -> - CreateDatabaseWithEncryptionKey.createDatabaseWithEncryptionKey( - databaseAdminClient, - projectId, - instanceId, - databaseId, - key - )); - assertThat(out).contains( - "Database projects/" + projectId + "/instances/" + instanceId + "/databases/" + databaseId - + " created with encryption key " + key); + 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( diff --git a/samples/snippets/src/test/java/com/example/spanner/GetDatabaseDdlSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/GetDatabaseDdlSampleIT.java index a345aa7156..a50f55e355 100644 --- a/samples/snippets/src/test/java/com/example/spanner/GetDatabaseDdlSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/GetDatabaseDdlSampleIT.java @@ -18,42 +18,44 @@ import static org.junit.Assert.assertTrue; -import com.google.cloud.spanner.InstanceConfig; -import com.google.cloud.spanner.InstanceConfigId; -import java.util.Arrays; +import com.google.common.collect.Lists; +import com.google.spanner.admin.database.v1.CreateDatabaseRequest; +import com.google.spanner.admin.instance.v1.InstanceConfig; +import com.google.spanner.admin.instance.v1.InstanceName; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class GetDatabaseDdlSampleIT extends SampleTestBase { +public class GetDatabaseDdlSampleIT extends SampleTestBaseV2 { @Test public void testGetDatabaseDdl() throws Exception { // Finds a possible new leader option - final InstanceConfigId instanceConfigId = instanceAdminClient - .getInstance(multiRegionalInstanceId) - .getInstanceConfigId(); - final InstanceConfig config = instanceAdminClient - .getInstanceConfig(instanceConfigId.getInstanceConfig()); + final String instanceConfigId = instanceAdminClient.getInstance( + InstanceName.of(projectId, multiRegionalInstanceId)).getConfig(); + final InstanceConfig config = instanceAdminClient.getInstanceConfig(instanceConfigId); assertTrue( "Expected instance config " + instanceConfigId + " to have at least one leader option", - config.getLeaderOptions().size() > 0 + config.getLeaderOptionsList().size() > 0 ); - final String defaultLeader = config.getLeaderOptions().get(0); + final String defaultLeader = config.getLeaderOptions(0); // Creates database final String databaseId = idGenerator.generateDatabaseId(); - databaseAdminClient.createDatabase( - multiRegionalInstanceId, - databaseId, - Arrays.asList( - "CREATE TABLE Singers (Id INT64 NOT NULL) PRIMARY KEY (Id)", - "ALTER DATABASE `" - + databaseId - + "` SET OPTIONS ( default_leader = '" - + defaultLeader - + "')" - ) - ).get(5, TimeUnit.MINUTES); + final CreateDatabaseRequest request = + CreateDatabaseRequest.newBuilder() + .setParent( + com.google.spanner.admin.database.v1.InstanceName.of(projectId, + multiRegionalInstanceId).toString()) + .setCreateStatement("CREATE DATABASE `" + databaseId + "`") + .addAllExtraStatements(Lists.newArrayList( + "CREATE TABLE Singers (Id INT64 NOT NULL) PRIMARY KEY (Id)", + "ALTER DATABASE `" + + databaseId + + "` SET OPTIONS ( default_leader = '" + + defaultLeader + + "')" + )).build(); + databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample(() -> GetDatabaseDdlSample diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/ListDatabasesIT.java b/samples/snippets/src/test/java/com/example/spanner/ListDatabasesIT.java similarity index 92% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/ListDatabasesIT.java rename to samples/snippets/src/test/java/com/example/spanner/ListDatabasesIT.java index 730205b8b4..ce3e56bc64 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/ListDatabasesIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/ListDatabasesIT.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner; import static org.junit.Assert.assertTrue; -import com.example.spanner.SampleRunner; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/samples/snippets/src/test/java/com/example/spanner/PgCaseSensitivitySampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgCaseSensitivitySampleIT.java index 1c2bca587c..fca4ac55c0 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgCaseSensitivitySampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgCaseSensitivitySampleIT.java @@ -18,24 +18,22 @@ import static org.junit.Assert.assertTrue; -import com.google.cloud.spanner.DatabaseId; -import com.google.cloud.spanner.Dialect; -import java.util.Collections; +import com.google.spanner.admin.database.v1.CreateDatabaseRequest; +import com.google.spanner.admin.database.v1.DatabaseDialect; +import com.google.spanner.admin.database.v1.InstanceName; import org.junit.Test; -public class PgCaseSensitivitySampleIT extends SampleTestBase { +public class PgCaseSensitivitySampleIT extends SampleTestBaseV2 { @Test public void testPgCaseSensitivitySample() throws Exception { final String databaseId = idGenerator.generateDatabaseId(); - databaseAdminClient - .createDatabase( - databaseAdminClient - .newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId)) - .setDialect(Dialect.POSTGRESQL) - .build(), - Collections.emptyList()) - .get(); + final CreateDatabaseRequest request = + CreateDatabaseRequest.newBuilder() + .setCreateStatement(getCreateDatabaseStatement(databaseId, DatabaseDialect.POSTGRESQL)) + .setParent(InstanceName.of(projectId, instanceId).toString()) + .setDatabaseDialect(DatabaseDialect.POSTGRESQL).build(); + databaseAdminClient.createDatabaseAsync(request).get(); final String out = SampleRunner.runSample( diff --git a/samples/snippets/src/test/java/com/example/spanner/PgInterleavedTableSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgInterleavedTableSampleIT.java index 6678f2a471..412147da0b 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgInterleavedTableSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgInterleavedTableSampleIT.java @@ -18,24 +18,22 @@ import static org.junit.Assert.assertTrue; -import com.google.cloud.spanner.DatabaseId; -import com.google.cloud.spanner.Dialect; -import java.util.Collections; +import com.google.spanner.admin.database.v1.CreateDatabaseRequest; +import com.google.spanner.admin.database.v1.DatabaseDialect; +import com.google.spanner.admin.database.v1.InstanceName; import org.junit.Test; -public class PgInterleavedTableSampleIT extends SampleTestBase { +public class PgInterleavedTableSampleIT extends SampleTestBaseV2 { @Test public void testPgInterleavedTableSample() throws Exception { final String databaseId = idGenerator.generateDatabaseId(); - databaseAdminClient - .createDatabase( - databaseAdminClient - .newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId)) - .setDialect(Dialect.POSTGRESQL) - .build(), - Collections.emptyList()) - .get(); + final CreateDatabaseRequest request = + CreateDatabaseRequest.newBuilder() + .setCreateStatement(getCreateDatabaseStatement(databaseId, DatabaseDialect.POSTGRESQL)) + .setParent(InstanceName.of(projectId, instanceId).toString()) + .setDatabaseDialect(DatabaseDialect.POSTGRESQL).build(); + databaseAdminClient.createDatabaseAsync(request).get(); final String out = SampleRunner.runSample( diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java index 796df092c8..d55af6203d 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java @@ -19,17 +19,18 @@ import static com.google.common.truth.Truth.assertThat; import com.google.cloud.Timestamp; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; +import 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.UUID; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; -import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,26 +41,23 @@ */ @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:abbreviationaswordinname") -public class PgSpannerSampleIT { +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"); - private static final String databaseId = formatForTest(baseDbId); - private static final String encryptedDatabaseId = formatForTest(baseDbId); - private static final String encryptedBackupId = formatForTest(baseDbId); - private static final String encryptedRestoreId = formatForTest(baseDbId); static Spanner spanner; static DatabaseId dbId; static DatabaseAdminClient dbClient; @BeforeClass - public static void setUp() { + public static void setUp() throws IOException { SpannerOptions options = SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build(); spanner = options.getService(); - dbClient = spanner.getDatabaseAdminClient(); - dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); + 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(); } @@ -68,34 +66,25 @@ static void deleteStaleTestDatabases() { Timestamp now = Timestamp.now(); Pattern samplePattern = getTestDbIdPattern(PgSpannerSampleIT.baseDbId); Pattern restoredPattern = getTestDbIdPattern("restored"); - for (Database db : dbClient.listDatabases(PgSpannerSampleIT.instanceId).iterateAll()) { + 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 (db.getId().getDatabase().length() >= DBID_LENGTH) { - if (samplePattern.matcher(toComparableId(PgSpannerSampleIT.baseDbId, - db.getId().getDatabase())).matches()) { - db.drop(); + if (databaseName.getDatabase().length() >= DBID_LENGTH) { + if (samplePattern.matcher( + toComparableId(PgSpannerSampleIT.baseDbId, databaseName.getDatabase())).matches()) { + dbClient.dropDatabase(db.getName()); } - if (restoredPattern.matcher(toComparableId("restored", db.getId().getDatabase())) + if (restoredPattern.matcher(toComparableId("restored", databaseName.getDatabase())) .matches()) { - db.drop(); + dbClient.dropDatabase(db.getName()); } } } } } - @AfterClass - public static void tearDown() { - dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); - dbClient.dropDatabase( - dbId.getInstanceId().getInstance(), SpannerSample.createRestoredSampleDbId(dbId)); - dbClient.dropDatabase(instanceId, encryptedDatabaseId); - dbClient.dropDatabase(instanceId, encryptedRestoreId); - dbClient.deleteBackup(instanceId, encryptedBackupId); - spanner.close(); - } - private static String toComparableId(String baseId, String existingId) { String zeroUuid = "00000000-0000-0000-0000-0000-00000000"; int shouldBeLength = (baseId + "-" + zeroUuid).length(); @@ -109,17 +98,13 @@ private static Pattern getTestDbIdPattern(String baseDbId) { Pattern.CASE_INSENSITIVE); } - static String formatForTest(String name) { - return name + "-" + UUID.randomUUID().toString().substring(0, DBID_LENGTH); - } - - private String runSample(String command) { + 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 + ":" + databaseId); - PgSpannerSample.main(new String[]{command, instanceId, databaseId}); + System.out.println(instanceId + ":" + dbId.getDatabase()); + PgSpannerSample.main(new String[]{command, instanceId, dbId.getDatabase()}); System.setOut(stdOut); return bout.toString(); } @@ -127,7 +112,7 @@ private String runSample(String command) { @Test public void testSample() throws Exception { assertThat(instanceId).isNotNull(); - assertThat(databaseId).isNotNull(); + assertThat(dbId.getDatabase()).isNotNull(); System.out.println("Create Database ..."); String out = runSample("createpgdatabase"); diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java index d8e5b0c9c5..c906006ef5 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java @@ -16,6 +16,7 @@ package com.example.spanner; +import static com.example.spanner.SpannerSampleIT.formatForTest; import static com.google.common.truth.Truth.assertThat; import com.google.api.gax.longrunning.OperationFuture; @@ -52,7 +53,7 @@ public class PgSpannerStandaloneExamplesIT { // The instance needs to exist for tests to pass. private static String instanceId = System.getProperty("spanner.test.instance"); private static String baseDatabaseId = System.getProperty("spanner.sample.database", "mysample"); - private static String databaseId = SpannerSampleIT.formatForTest(baseDatabaseId); + private static String databaseId = formatForTest(baseDatabaseId); private static DatabaseId dbId; private static DatabaseAdminClient dbClient; private static Spanner spanner; @@ -157,7 +158,7 @@ public void addNumericColumn_shouldSuccessfullyAddColumn() () -> { try { AddNumericColumnSample.addNumericColumn( - spanner.getDatabaseAdminClient(), instanceId, databaseId); + spanner.getOptions().getProjectId(), instanceId, databaseId); } catch (ExecutionException e) { System.out.printf( "Adding column `Revenue` failed: %s%n", e.getCause().getMessage()); @@ -226,7 +227,7 @@ public void addJsonbColumn_shouldSuccessfullyAddColumn() () -> { try { AddJsonbColumnSample.addJsonbColumn( - spanner.getDatabaseAdminClient(), instanceId, databaseId); + spanner.getOptions().getProjectId(), instanceId, databaseId); } catch (ExecutionException e) { System.out.printf( "Adding column `VenueDetails` failed: %s%n", e.getCause().getMessage()); diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/SampleTestBaseV2.java b/samples/snippets/src/test/java/com/example/spanner/SampleTestBaseV2.java similarity index 99% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/SampleTestBaseV2.java rename to samples/snippets/src/test/java/com/example/spanner/SampleTestBaseV2.java index 32e1c26fcb..909e79738b 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/SampleTestBaseV2.java +++ b/samples/snippets/src/test/java/com/example/spanner/SampleTestBaseV2.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner; import com.example.spanner.SampleIdGenerator; import com.google.cloud.spanner.Spanner; diff --git a/samples/snippets/src/test/java/com/example/spanner/SequenceSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/SequenceSampleIT.java index c8174ff4ca..de2c3961c2 100644 --- a/samples/snippets/src/test/java/com/example/spanner/SequenceSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/SequenceSampleIT.java @@ -20,10 +20,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import com.google.cloud.spanner.DatabaseId; -import com.google.cloud.spanner.Dialect; import com.google.common.collect.ImmutableList; -import java.util.Collections; +import com.google.spanner.admin.database.v1.CreateDatabaseRequest; +import com.google.spanner.admin.database.v1.DatabaseDialect; import java.util.HashSet; import java.util.concurrent.TimeUnit; import org.junit.Before; @@ -38,23 +37,23 @@ * dialects. */ @RunWith(Parameterized.class) -public class SequenceSampleIT extends SampleTestBase { +public class SequenceSampleIT extends SampleTestBaseV2 { - private static DatabaseId databaseId; + private static String databaseId; /** * Set of dialects for which database has already been created in this test suite. This helps in * limiting the number of databases created per dialect to one. */ - private static final HashSet dbInitializedDialects = new HashSet<>(); + private static final HashSet dbInitializedDialects = new HashSet<>(); @Parameters(name = "dialect = {0}") - public static Iterable data() { - return ImmutableList.of(Dialect.GOOGLE_STANDARD_SQL, Dialect.POSTGRESQL); + public static Iterable data() { + return ImmutableList.of(DatabaseDialect.GOOGLE_STANDARD_SQL, DatabaseDialect.POSTGRESQL); } @Parameter(0) - public static Dialect dialect; + public static DatabaseDialect dialect; @Before public void createTestDatabase() throws Exception { @@ -63,37 +62,36 @@ public void createTestDatabase() throws Exception { return; } dbInitializedDialects.add(dialect); - final String database = idGenerator.generateDatabaseId(); + databaseId = idGenerator.generateDatabaseId(); + CreateDatabaseRequest createDatabaseRequest = + CreateDatabaseRequest.newBuilder() + .setParent(getInstanceName(projectId, instanceId)) + .setCreateStatement(getCreateDatabaseStatement(databaseId, dialect)) + .setDatabaseDialect(dialect).build(); databaseAdminClient - .createDatabase( - databaseAdminClient - .newDatabaseBuilder(DatabaseId.of(projectId, instanceId, database)) - .setDialect(dialect) - .build(), - Collections.emptyList()) + .createDatabaseAsync(createDatabaseRequest) .get(10, TimeUnit.MINUTES); - databaseId = DatabaseId.of(projectId, instanceId, database); } @Test public void createSequence() throws Exception { String out; - if (dialect == Dialect.GOOGLE_STANDARD_SQL) { + if (dialect == DatabaseDialect.GOOGLE_STANDARD_SQL) { out = runSample( () -> CreateSequenceSample.createSequence( - projectId, instanceId, databaseId.getDatabase())); + projectId, instanceId, databaseId)); } else { out = runSample( () -> PgCreateSequenceSample.pgCreateSequence( - projectId, instanceId, databaseId.getDatabase())); + projectId, instanceId, databaseId)); } assertTrue( out.contains( - "Created Seq sequence and Customers table, where its key column " + "Created Seq sequence and Customers table, where the key column " + "CustomerId uses the sequence as a default value")); assertEquals(out.split("Inserted customer record with CustomerId", -1).length - 1, 3); assertTrue(out.contains("Number of customer records inserted is: 3")); @@ -102,18 +100,18 @@ public void createSequence() throws Exception { @Test public void alterSequence() throws Exception { String out; - if (dialect == Dialect.GOOGLE_STANDARD_SQL) { + if (dialect == DatabaseDialect.GOOGLE_STANDARD_SQL) { out = runSample( () -> AlterSequenceSample.alterSequence( - projectId, instanceId, databaseId.getDatabase())); + projectId, instanceId, databaseId)); } else { out = runSample( () -> PgAlterSequenceSample.pgAlterSequence( - projectId, instanceId, databaseId.getDatabase())); + projectId, instanceId, databaseId)); } assertTrue( out.contains("Altered Seq sequence to skip an inclusive range between 1000 and 5000000")); @@ -124,17 +122,17 @@ public void alterSequence() throws Exception { @Test public void dropSequence() throws Exception { String out; - if (dialect == Dialect.GOOGLE_STANDARD_SQL) { + if (dialect == DatabaseDialect.GOOGLE_STANDARD_SQL) { out = runSample( () -> - DropSequenceSample.dropSequence(projectId, instanceId, databaseId.getDatabase())); + DropSequenceSample.dropSequence(projectId, instanceId, databaseId)); } else { out = runSample( () -> PgDropSequenceSample.pgDropSequence( - projectId, instanceId, databaseId.getDatabase())); + projectId, instanceId, databaseId)); } assertTrue( out.contains( diff --git a/samples/snippets/src/test/java/com/example/spanner/SpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/SpannerSampleIT.java index 72e8c36d76..422d7618de 100644 --- a/samples/snippets/src/test/java/com/example/spanner/SpannerSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/SpannerSampleIT.java @@ -20,10 +20,6 @@ import static org.junit.Assert.assertTrue; import com.google.cloud.Timestamp; -import com.google.cloud.spanner.Backup; -import com.google.cloud.spanner.BackupId; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.Instance; @@ -36,26 +32,35 @@ 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; -import org.threeten.bp.LocalDate; -import org.threeten.bp.temporal.ChronoField; -/** Unit tests for {@code SpannerSample} */ +/** + * Unit tests for {@code SpannerSample} + */ @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:abbreviationaswordinname") -public class SpannerSampleIT { +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"); @@ -66,24 +71,30 @@ public class SpannerSampleIT { 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 databaseId = formatForTest(baseDbId); - private static final String encryptedDatabaseId = formatForTest(baseDbId); private static final String encryptedBackupId = formatForTest(baseDbId); - private static final String encryptedRestoreId = formatForTest(baseDbId); private static final long STALE_INSTANCE_THRESHOLD_SECS = TimeUnit.SECONDS.convert(24L, TimeUnit.HOURS); static Spanner spanner; - static DatabaseId dbId; - static DatabaseAdminClient dbClient; + static DatabaseAdminClient databaseAdminClient; private static String key; private long lastUpdateDataTimeInMillis; - private String runSample(String command) throws Exception { + 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}); + 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(); } @@ -93,10 +104,9 @@ public static void setUp() throws Exception { SpannerOptions options = SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build(); spanner = options.getService(); - dbClient = spanner.getDatabaseAdminClient(); - dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); + databaseAdminClient = DatabaseAdminClient.create(); // Delete stale test databases that have been created earlier by this test, but not deleted. - deleteStaleTestDatabases(instanceId, baseDbId); + deleteStaleTestDatabases(); key = String.format( "projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", @@ -132,20 +142,25 @@ private static void deleteStaleEncryptedTestInstances() throws InterruptedExcept } } - static void deleteStaleTestDatabases(String instanceId, String baseDbId) { + static void deleteStaleTestDatabases() throws IOException { Timestamp now = Timestamp.now(); - Pattern samplePattern = getTestDbIdPattern(baseDbId); + Pattern samplePattern = getTestDbIdPattern(SpannerSampleIT.baseDbId); Pattern restoredPattern = getTestDbIdPattern("restored"); - for (Database db : dbClient.listDatabases(instanceId).iterateAll()) { - if (TimeUnit.HOURS.convert(now.getSeconds() - db.getCreateTime().getSeconds(), - TimeUnit.SECONDS) > 24) { - if (db.getId().getDatabase().length() >= DBID_LENGTH) { - if (samplePattern.matcher(toComparableId(baseDbId, db.getId().getDatabase())).matches()) { - db.drop(); - } - if (restoredPattern.matcher(toComparableId("restored", db.getId().getDatabase())) - .matches()) { - db.drop(); + 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()); + } } } } @@ -153,305 +168,355 @@ static void deleteStaleTestDatabases(String instanceId, String baseDbId) { } @AfterClass - public static void tearDown() throws Exception { - dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); - dbClient.dropDatabase( - dbId.getInstanceId().getInstance(), SpannerSample.createRestoredSampleDbId(dbId)); - dbClient.dropDatabase(instanceId, encryptedDatabaseId); - dbClient.dropDatabase(instanceId, encryptedRestoreId); - dbClient.deleteBackup(instanceId, encryptedBackupId); + 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"); + String out = runSample("createdatabase", databaseId); assertThat(out).contains("Created database"); assertThat(out).contains(dbId.getName()); - runSample("write"); + System.out.println("Write data to sample tables ..."); + runSample("write", databaseId); - out = runSample("delete"); + System.out.println("Delete data to sample tables ..."); + out = runSample("delete", databaseId); assertThat(out).contains("Records deleted."); - runSample("write"); + runSample("write", databaseId); - out = runSample("read"); + System.out.println("Read data from sample tables ..."); + out = runSample("read", databaseId); assertThat(out).contains("1 1 Total Junk"); - out = runSample("query"); + out = runSample("query", databaseId); assertThat(out).contains("1 1 Total Junk"); - runSample("addmarketingbudget"); + 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"); - out = runSample("readstaledata"); + runSample("update", databaseId); + + System.out.println("Read stale data from sample tables ..."); + out = runSample("readstaledata", databaseId); assertThat(out).contains("1 1 NULL"); - runSample("writetransaction"); - out = runSample("querymarketingbudget"); + 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"); - runSample("addindex"); - out = runSample("queryindex"); + 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"); - out = runSample("readindex"); + 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"); - runSample("addstoringindex"); - out = runSample("readstoringindex"); + System.out.println("Add Storing index ..."); + runSample("addstoringindex", databaseId); + out = runSample("readstoringindex", databaseId); assertThat(out).contains("300000"); - out = runSample("readonlytransaction"); + System.out.println("Read storing index ..."); + out = runSample("readonlytransaction", databaseId); assertThat(out.replaceAll("[\r\n]+", " ")).containsMatch("(Total Junk.*){2}"); - out = runSample("addcommittimestamp"); + out = runSample("addcommittimestamp", databaseId); assertThat(out).contains("Added LastUpdateTime as a commit timestamp column"); - runSample("updatewithtimestamp"); - out = runSample("querywithtimestamp"); + runSample("updatewithtimestamp", databaseId); + out = runSample("querywithtimestamp", databaseId); assertThat(out).contains("1 1 1000000"); assertThat(out).contains("2 2 750000"); - out = runSample("createtablewithtimestamp"); + out = runSample("createtablewithtimestamp", databaseId); assertThat(out).contains("Created Performances table in database"); - runSample("writewithtimestamp"); - out = runSample("queryperformancestable"); + 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"); - out = runSample("querywithstruct"); + runSample("writestructdata", databaseId); + out = runSample("querywithstruct", databaseId); assertThat(out).startsWith("6\n"); - out = runSample("querywitharrayofstruct"); + out = runSample("querywitharrayofstruct", databaseId); assertThat(out).startsWith("8\n7\n6"); - out = runSample("querystructfield"); + out = runSample("querystructfield", databaseId); assertThat(out).startsWith("6\n"); - out = runSample("querynestedstructfield"); + out = runSample("querynestedstructfield", databaseId); assertThat(out).contains("6 Imagination\n"); assertThat(out).contains("9 Imagination\n"); - runSample("insertusingdml"); - out = runSample("querysingerstable"); + runSample("insertusingdml", databaseId); + out = runSample("querysingerstable", databaseId); assertThat(out).contains("Virginia Watson"); - runSample("updateusingdml"); - out = runSample("querymarketingbudget"); + runSample("updateusingdml", databaseId); + out = runSample("querymarketingbudget", databaseId); assertThat(out).contains("1 1 2000000"); - runSample("deleteusingdml"); - out = runSample("querysingerstable"); + runSample("deleteusingdml", databaseId); + out = runSample("querysingerstable", databaseId); assertThat(out).doesNotContain("Alice Trentor"); - out = runSample("updateusingdmlwithtimestamp"); + out = runSample("updateusingdmlwithtimestamp", databaseId); assertThat(out).contains("2 records updated"); - out = runSample("writeandreadusingdml"); + out = runSample("writeandreadusingdml", databaseId); assertThat(out).contains("Timothy Campbell"); - runSample("updateusingdmlwithstruct"); - out = runSample("querysingerstable"); + runSample("updateusingdmlwithstruct", databaseId); + out = runSample("querysingerstable", databaseId); assertThat(out).contains("Timothy Grant"); - runSample("writeusingdml"); - out = runSample("querysingerstable"); + 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"); + out = runSample("querywithparameter", databaseId); assertThat(out).contains("12 Melissa Garcia"); - runSample("writewithtransactionusingdml"); - out = runSample("querymarketingbudget"); + runSample("writewithtransactionusingdml", databaseId); + out = runSample("querymarketingbudget", databaseId); assertThat(out).contains("1 1 2200000"); assertThat(out).contains("2 2 550000"); - runSample("updateusingpartitioneddml"); - out = runSample("querymarketingbudget"); + runSample("updateusingpartitioneddml", databaseId); + out = runSample("querymarketingbudget", databaseId); assertThat(out).contains("1 1 2200000"); assertThat(out).contains("2 2 100000"); - runSample("deleteusingpartitioneddml"); - out = runSample("querysingerstable"); + 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"); + 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"); + out = runSample("createtablewithdatatypes", databaseId); assertThat(out).contains("Created Venues table in database"); - runSample("writedatatypesdata"); - out = runSample("querywitharray"); + 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"); + out = runSample("querywithbool", databaseId); assertThat(out).contains("19 Venue 19 true"); - out = runSample("querywithbytes"); + out = runSample("querywithbytes", databaseId); assertThat(out).contains("4 Venue 4"); - out = runSample("querywithdate"); + 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"); + out = runSample("querywithfloat", databaseId); assertThat(out).contains("4 Venue 4 0.8"); assertThat(out).contains("19 Venue 19 0.9"); - out = runSample("querywithint"); + out = runSample("querywithint", databaseId); assertThat(out).contains("19 Venue 19 6300"); assertThat(out).contains("42 Venue 42 3000"); - out = runSample("querywithstring"); + out = runSample("querywithstring", databaseId); assertThat(out).contains("42 Venue 42"); - out = runSample("querywithtimestampparameter"); + 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"); + out = runSample("querywithnumeric", databaseId); assertThat(out).contains("19 Venue 19 1200100"); assertThat(out).contains("42 Venue 42 390650.99"); - out = runSample("clientwithqueryoptions"); + out = runSample("clientwithqueryoptions", databaseId); assertThat(out).contains("1 1 Total Junk"); - out = runSample("querywithqueryoptions"); + out = runSample("querywithqueryoptions", databaseId); assertThat(out).contains("1 1 Total Junk"); + } - String backupName = - String.format( - "%s_%02d", - dbId.getDatabase(), LocalDate.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR)); - BackupId backupId = BackupId.of(dbId.getInstanceId(), backupName); + @Test + public void testBackupSamples_withoutEncryption() { + String databaseId = idGenerator.generateDatabaseId(); + DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId); + String restoreDatabaseId = idGenerator.generateDatabaseId(); + String backupId = idGenerator.generateBackupId(); - out = runSample("createbackup"); - assertThat(out).contains("Created backup [" + backupId + "]"); + try { + assertThat(instanceId).isNotNull(); + assertThat(databaseId).isNotNull(); - out = runSample("cancelcreatebackup"); - assertThat(out).contains( - "Backup operation for [" + backupId + "_cancel] successfully"); + System.out.println("Creating Database ..."); + String out = runSample("createdatabase", databaseId); + assertThat(out).contains("Created database"); + assertThat(out).contains(dbId.getName()); - // TODO: remove try-catch when filtering on metadata fields works. - try { - out = runSample("listbackupoperations"); - assertThat(out).contains( - String.format( - "Backup %s on database %s pending:", - backupId.getName(), - dbId.getName())); - assertTrue("Out does not contain copy backup operations", out.contains( - "Copy Backup Operations")); - } catch (SpannerException e) { - assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); - assertThat(e.getMessage()).contains("Cannot evaluate filter expression"); - } + BackupName backupName = BackupName.of(projectId, instanceId, backupId); - out = runSample("listbackups"); - assertThat(out).contains("All backups:"); - assertThat(out).contains( - String.format("All backups with backup name containing \"%s\":", backupId.getBackup())); - 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 7 times. - assertThat(countOccurrences(out, backupId.getName())).isEqualTo(7); - - // 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) { + 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 { - out = runSample("restorebackup"); + System.out.println("List Backup Operations ..."); + out = runSample("listbackupoperations", databaseId, backupId); assertThat(out).contains( - "Restored database [" - + dbId.getName() - + "] from [" - + backupId.getName() - + "]"); - restored = true; - break; + 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) { - 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; + 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; } - Uninterruptibles.sleepUninterruptibly(60L, TimeUnit.SECONDS); - } else { - throw e; } } - } - if (restored) { - out = runSample("listdatabaseoperations"); + 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( - "Database %s restored from backup", - DatabaseId.of( - dbId.getInstanceId(), - SpannerSample.createRestoredSampleDbId(dbId)) - .getName())); + 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(); - out = runSample("updatebackup"); - assertThat(out).contains( - String.format("Updated backup [" + backupId + "]")); + String out = runSample("createdatabase", databaseId); + assertThat(out).contains("Created database"); + assertThat(out).contains(dbId.getName()); - // 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. - dbClient.dropDatabase( - dbId.getInstanceId().getInstance(), SpannerSample.createRestoredSampleDbId(dbId)); + String backupId = idGenerator.generateBackupId(); - out = runSample("deletebackup"); - assertThat(out).contains("Deleted backup [" + backupId + "]"); + 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 = String.format("encrypted-test-%s", UUID.randomUUID()); + String instanceId = idGenerator.generateInstanceId(); InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); instanceAdminClient .createInstance(InstanceInfo.newBuilder(InstanceId.of(projectId, instanceId)) @@ -461,15 +526,15 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { .get(); try { String out = SampleRunner - .runSample(() -> CreateDatabaseWithEncryptionKey.createDatabaseWithEncryptionKey(dbClient, - projectId, instanceId, encryptedDatabaseId, key)); + .runSample(() -> SpannerSample.createDatabase( + databaseAdminClient, InstanceName.of(projectId, instanceId), databaseId)); assertThat(out).contains(String.format( - "Database projects/%s/instances/%s/databases/%s created with encryption key %s", - projectId, instanceId, encryptedDatabaseId, key)); + "Created database [%s]", DatabaseName.of(projectId, instanceId, databaseId))); out = SampleRunner.runSampleWithRetry( - () -> CreateBackupWithEncryptionKey.createBackupWithEncryptionKey(dbClient, projectId, - instanceId, encryptedDatabaseId, encryptedBackupId, key), + () -> 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 " @@ -477,14 +542,14 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { projectId, instanceId, encryptedBackupId, key)); out = SampleRunner.runSampleWithRetry( - () -> RestoreBackupWithEncryptionKey.restoreBackupWithEncryptionKey(dbClient, projectId, - instanceId, encryptedBackupId, encryptedRestoreId, key), + () -> 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, encryptedDatabaseId, projectId, instanceId, encryptedRestoreId, + 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 @@ -494,13 +559,43 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { } } + @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 { - for (Backup backup : dbClient.listBackups(instanceId).iterateAll()) { + InstanceName instanceName = InstanceName.of(projectId, instanceId); + for (Backup backup : databaseAdminClient.listBackups(instanceName.toString()).iterateAll()) { int attempts = 0; while (attempts < 30) { try { attempts++; - backup.delete(); + databaseAdminClient.deleteBackup(backup.getName()); break; } catch (SpannerException e) { if (e.getErrorCode() == ErrorCode.FAILED_PRECONDITION && e.getMessage() @@ -528,6 +623,9 @@ private String runSampleRunnable(Runnable sample) { @Test public void testCreateInstanceSample() { + String databaseId = idGenerator.generateDatabaseId(); + DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId); + String instanceId = formatForTest("sample-inst"); String out = runSampleRunnable(() -> { @@ -567,6 +665,7 @@ static String formatForTest(String name) { } static class ShouldRetryBackupOperation implements Predicate { + private static final int MAX_ATTEMPTS = 20; private int attempts = 0; diff --git a/samples/snippets/src/test/java/com/example/spanner/SpannerStandaloneExamplesIT.java b/samples/snippets/src/test/java/com/example/spanner/SpannerStandaloneExamplesIT.java index 7c059bec1f..0acbf19893 100644 --- a/samples/snippets/src/test/java/com/example/spanner/SpannerStandaloneExamplesIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/SpannerStandaloneExamplesIT.java @@ -16,11 +16,20 @@ package com.example.spanner; +import static com.example.spanner.SpannerSampleIT.formatForTest; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import com.example.spanner.CustomTimeoutAndRetrySettingsExample; +import com.example.spanner.QueryWithJsonParameterSample; +import com.example.spanner.QueryWithNumericParameterSample; +import com.example.spanner.StatementTimeoutExample; +import com.example.spanner.TransactionTimeoutExample; +import com.example.spanner.UpdateJsonDataSample; +import com.example.spanner.UpdateNumericDataSample; +import com.example.spanner.admin.archived.SpannerSampleIT; import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; @@ -56,7 +65,7 @@ public class SpannerStandaloneExamplesIT { // The instance needs to exist for tests to pass. private static String instanceId = System.getProperty("spanner.test.instance"); private static String baseDatabaseId = System.getProperty("spanner.sample.database", "mysample"); - private static String databaseId = SpannerSampleIT.formatForTest(baseDatabaseId); + private static String databaseId = formatForTest(baseDatabaseId); private static DatabaseId dbId; private static DatabaseAdminClient dbClient; private static Spanner spanner; @@ -183,7 +192,7 @@ public void addNumericColumn_shouldSuccessfullyAddColumn() () -> { try { AddNumericColumnSample.addNumericColumn( - spanner.getDatabaseAdminClient(), instanceId, databaseId); + spanner.getOptions().getProjectId(), instanceId, databaseId); } catch (ExecutionException e) { System.out.printf( "Adding column `Revenue` failed: %s%n", e.getCause().getMessage()); @@ -252,7 +261,7 @@ public void addJsonColumn_shouldSuccessfullyAddColumn() () -> { try { AddJsonColumnSample.addJsonColumn( - spanner.getDatabaseAdminClient(), instanceId, databaseId); + spanner.getOptions().getProjectId(), instanceId, databaseId); } catch (ExecutionException e) { System.out.printf( "Adding column `VenueDetails` failed: %s%n", e.getCause().getMessage()); diff --git a/samples/snippets/src/test/java/com/example/spanner/UpdateDatabaseSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/UpdateDatabaseSampleIT.java index 30ad8eec6a..b53414a2f5 100644 --- a/samples/snippets/src/test/java/com/example/spanner/UpdateDatabaseSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/UpdateDatabaseSampleIT.java @@ -20,40 +20,51 @@ import static org.junit.Assert.assertTrue; import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.DatabaseId; -import com.google.cloud.spanner.DatabaseInfo.DatabaseField; +import com.google.common.collect.Lists; +import com.google.protobuf.FieldMask; +import com.google.spanner.admin.database.v1.Database; +import com.google.spanner.admin.database.v1.DatabaseName; import com.google.spanner.admin.database.v1.UpdateDatabaseMetadata; -import java.util.Collections; +import com.google.spanner.admin.database.v1.UpdateDatabaseRequest; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class UpdateDatabaseSampleIT extends SampleTestBase { +public class UpdateDatabaseSampleIT extends SampleTestBaseV2 { @Test public void testUpdateDatabase() throws Exception { // Create database final String databaseId = idGenerator.generateDatabaseId(); databaseAdminClient - .createDatabase(instanceId, databaseId, Collections.emptyList()) + .createDatabaseAsync(getInstanceName(projectId, instanceId), + "CREATE DATABASE `" + databaseId + "`") .get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample( () -> UpdateDatabaseSample.updateDatabase(projectId, instanceId, databaseId)); - - DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId); assertTrue( "Expected that database would have been updated. Output received was " + out, - out.contains(String.format("Updated database %s", dbId))); + out.contains(String.format( + "Updated database %s", DatabaseName.of(projectId, instanceId, databaseId)))); // Cleanup - Database databaseToUpdate = - databaseAdminClient.newDatabaseBuilder(dbId).disableDropProtection().build(); + final com.google.spanner.admin.database.v1.Database database = + com.google.spanner.admin.database.v1.Database.newBuilder() + .setName(DatabaseName.of(projectId, instanceId, databaseId).toString()) + .setEnableDropProtection(false).build(); + final UpdateDatabaseRequest updateDatabaseRequest = + UpdateDatabaseRequest.newBuilder() + .setDatabase(database) + .setUpdateMask( + FieldMask.newBuilder().addAllPaths( + Lists.newArrayList("enable_drop_protection")).build()) + .build(); + OperationFuture operation = - databaseAdminClient.updateDatabase(databaseToUpdate, DatabaseField.DROP_PROTECTION); + databaseAdminClient.updateDatabaseAsync(updateDatabaseRequest); Database updatedDb = operation.get(5, TimeUnit.MINUTES); - assertFalse(updatedDb.isDropProtectionEnabled()); + assertFalse(updatedDb.getEnableDropProtection()); } } diff --git a/samples/snippets/src/test/java/com/example/spanner/UpdateDatabaseWithDefaultLeaderSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/UpdateDatabaseWithDefaultLeaderSampleIT.java index ede90a5ec3..2953e02935 100644 --- a/samples/snippets/src/test/java/com/example/spanner/UpdateDatabaseWithDefaultLeaderSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/UpdateDatabaseWithDefaultLeaderSampleIT.java @@ -18,38 +18,35 @@ import static org.junit.Assert.assertTrue; -import com.google.cloud.spanner.Database; -import com.google.cloud.spanner.InstanceConfig; -import com.google.cloud.spanner.InstanceConfigId; -import java.util.Collections; +import com.google.spanner.admin.database.v1.Database; +import com.google.spanner.admin.instance.v1.InstanceConfig; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class UpdateDatabaseWithDefaultLeaderSampleIT extends SampleTestBase { +public class UpdateDatabaseWithDefaultLeaderSampleIT extends SampleTestBaseV2 { @Test public void testUpdateDatabaseWithDefaultLeader() throws Exception { // Create database final String databaseId = idGenerator.generateDatabaseId(); final Database createdDatabase = databaseAdminClient - .createDatabase(multiRegionalInstanceId, databaseId, Collections.emptyList()) + .createDatabaseAsync(getInstanceName(projectId, multiRegionalInstanceId), + "CREATE DATABASE `" + databaseId + "`") .get(5, TimeUnit.MINUTES); final String defaultLeader = createdDatabase.getDefaultLeader(); // Finds a possible new leader option - final InstanceConfigId instanceConfigId = instanceAdminClient - .getInstance(multiRegionalInstanceId) - .getInstanceConfigId(); - final InstanceConfig config = instanceAdminClient - .getInstanceConfig(instanceConfigId.getInstanceConfig()); - final String newLeader = config - .getLeaderOptions() - .stream() - .filter(leader -> !leader.equals(defaultLeader)) - .findFirst() - .orElseThrow(() -> - new RuntimeException("Expected to find a leader option different than " + defaultLeader) - ); + final String instanceConfigId = + instanceAdminClient.getInstance(getInstanceName(projectId, multiRegionalInstanceId)) + .getConfig(); + final InstanceConfig config = instanceAdminClient.getInstanceConfig(instanceConfigId); + final String newLeader = + config.getLeaderOptionsList().stream() + .filter(leader -> !leader.equals(defaultLeader)) + .findFirst().orElseThrow(() -> + new RuntimeException("Expected to find a leader option different than " + + defaultLeader) + ); // Runs sample final String out = SampleRunner.runSample(() -> UpdateDatabaseWithDefaultLeaderSample diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/AlterTableWithForeignKeyDeleteCascadeSampleIT.java similarity index 77% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/AlterTableWithForeignKeyDeleteCascadeSampleIT.java index 2295119b69..89c10cc460 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/AlterTableWithForeignKeyDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/AlterTableWithForeignKeyDeleteCascadeSampleIT.java @@ -14,31 +14,28 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; import com.example.spanner.SampleTestBase; -import com.google.spanner.admin.database.v1.CreateDatabaseRequest; -import com.google.spanner.admin.database.v1.DatabaseDialect; -import com.google.spanner.admin.database.v1.InstanceName; import java.util.Arrays; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class AlterTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBaseV2 { +public class AlterTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBase { @Test public void testAlterTableWithForeignKeyDeleteCascade() throws Exception { // Creates database final String databaseId = idGenerator.generateDatabaseId(); - final CreateDatabaseRequest request = - CreateDatabaseRequest.newBuilder() - .setCreateStatement("CREATE DATABASE `" + databaseId + "`") - .setParent(InstanceName.of(projectId, instanceId).toString()) - .addAllExtraStatements(Arrays.asList( + databaseAdminClient + .createDatabase( + instanceId, + databaseId, + Arrays.asList( "CREATE TABLE Customers (\n" + " CustomerId INT64 NOT NULL,\n" + " CustomerName STRING(62) NOT NULL,\n" @@ -50,15 +47,15 @@ public void testAlterTableWithForeignKeyDeleteCascade() throws Exception { + " CONSTRAINT FKShoppingCartsCustomerId" + " FOREIGN KEY (CustomerId)\n" + " REFERENCES Customers (CustomerId)\n" - + " ) PRIMARY KEY (CartId)\n")).build(); - databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); + + " ) PRIMARY KEY (CartId)\n")) + .get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample( () -> AlterTableWithForeignKeyDeleteCascadeSample.alterForeignKeyDeleteCascadeConstraint( - projectId, instanceId, databaseId)); + databaseAdminClient, instanceId, databaseId)); assertTrue( "Expected to have created database " diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSampleIT.java new file mode 100644 index 0000000000..12b7c5044d --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateDatabaseWithDefaultLeaderSampleIT.java @@ -0,0 +1,61 @@ +/* + * 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.archived; + +import static org.junit.Assert.assertTrue; + +import com.example.spanner.SampleRunner; +import com.example.spanner.SampleTestBase; +import com.google.cloud.spanner.InstanceConfig; +import com.google.cloud.spanner.InstanceConfigId; +import org.junit.Test; + +public class CreateDatabaseWithDefaultLeaderSampleIT extends SampleTestBase { + + @Test + public void testCreateDatabaseWithDefaultLeader() throws Exception { + final String databaseId = idGenerator.generateDatabaseId(); + + // Finds possible default leader + final InstanceConfigId instanceConfigId = instanceAdminClient + .getInstance(multiRegionalInstanceId) + .getInstanceConfigId(); + final InstanceConfig config = instanceAdminClient + .getInstanceConfig(instanceConfigId.getInstanceConfig()); + assertTrue( + "Expected instance config " + instanceConfigId + " to have at least one leader option", + config.getLeaderOptions().size() > 0 + ); + final String defaultLeader = config.getLeaderOptions().get(0); + + // Runs sample + final String out = SampleRunner.runSample(() -> + CreateDatabaseWithDefaultLeaderSample.createDatabaseWithDefaultLeader( + projectId, + multiRegionalInstanceId, + databaseId, + defaultLeader + ) + ); + + assertTrue( + "Expected created database to have default leader " + defaultLeader + "." + + " Output received was " + out, + out.contains("Default leader: " + defaultLeader) + ); + } +} diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateDatabaseWithVersionRetentionPeriodSampleIT.java similarity index 84% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateDatabaseWithVersionRetentionPeriodSampleIT.java index 64400fbf21..448b1c10c4 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateDatabaseWithVersionRetentionPeriodSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateDatabaseWithVersionRetentionPeriodSampleIT.java @@ -14,20 +14,21 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static com.google.common.truth.Truth.assertThat; import com.example.spanner.SampleRunner; +import com.example.spanner.SampleTestBase; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** - * Integration tests for {@link com.example.spanner.CreateDatabaseWithVersionRetentionPeriodSample} + * Integration tests for {@link CreateDatabaseWithVersionRetentionPeriodSample} */ @RunWith(JUnit4.class) -public class CreateDatabaseWithVersionRetentionPeriodSampleIT extends SampleTestBaseV2 { +public class CreateDatabaseWithVersionRetentionPeriodSampleIT extends SampleTestBase { @Test public void createsDatabaseWithVersionRetentionPeriod() throws Exception { @@ -36,7 +37,7 @@ public void createsDatabaseWithVersionRetentionPeriod() throws Exception { final String out = SampleRunner.runSample(() -> CreateDatabaseWithVersionRetentionPeriodSample .createDatabaseWithVersionRetentionPeriod( - projectId, instanceId, databaseId, versionRetentionPeriod + databaseAdminClient, instanceId, databaseId, versionRetentionPeriod )); assertThat(out).contains( diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigSampleIT.java similarity index 58% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigSampleIT.java index 86224cfd30..041d0c5201 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateInstanceWithAutoscalingConfigSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateInstanceWithAutoscalingConfigSampleIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,24 +14,31 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static com.google.common.truth.Truth.assertThat; import com.example.spanner.SampleRunner; -import com.google.spanner.admin.database.v1.InstanceName; +import com.example.spanner.SampleTestBase; +import com.google.cloud.spanner.InstanceId; +import java.util.UUID; import org.junit.Test; -public class CreateInstanceWithAutoscalingConfigSampleIT extends SampleTestBaseV2 { +public class CreateInstanceWithAutoscalingConfigSampleIT extends SampleTestBase { @Test public void testCreateInstanceWithAutoscalingConfig() throws Exception { - String instanceId = idGenerator.generateInstanceId(); + String instanceId = String.format("autoscaler-%s", UUID.randomUUID()); String out = SampleRunner.runSample( - () -> CreateInstanceWithAutoscalingConfigExample.createInstance(projectId, instanceId)); + () -> { + try { + CreateInstanceWithAutoscalingConfigExample.createInstance(projectId, instanceId); + } finally { + spanner.getInstanceAdminClient().deleteInstance(instanceId); + } + }); assertThat(out) - .contains(String.format("Autoscaler instance %s", - InstanceName.of(projectId, instanceId).toString())); + .contains(String.format("Autoscaler instance %s", InstanceId.of(projectId, instanceId))); } } diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateTableWithForeignKeyDeleteCascadeSampleIT.java similarity index 71% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateTableWithForeignKeyDeleteCascadeSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateTableWithForeignKeyDeleteCascadeSampleIT.java index 6fd9aeddb1..a6cdee1947 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CreateTableWithForeignKeyDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CreateTableWithForeignKeyDeleteCascadeSampleIT.java @@ -14,37 +14,34 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; import com.example.spanner.SampleTestBase; -import com.google.spanner.admin.database.v1.CreateDatabaseRequest; -import com.google.spanner.admin.database.v1.InstanceName; import java.util.Arrays; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class CreateTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBaseV2 { +public class CreateTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBase { @Test public void testCreateTableWithForeignKeyDeleteCascade() throws Exception { // Creates database final String databaseId = idGenerator.generateDatabaseId(); - final CreateDatabaseRequest request = - CreateDatabaseRequest.newBuilder() - .setCreateStatement("CREATE DATABASE `" + databaseId + "`") - .setParent(InstanceName.of(projectId, instanceId).toString()).build(); - databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); + databaseAdminClient + .createDatabase(instanceId, databaseId, Arrays.asList()) + .get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample( () -> CreateTableWithForeignKeyDeleteCascadeSample - .createForeignKeyDeleteCascadeConstraint(projectId, instanceId, databaseId)); + .createForeignKeyDeleteCascadeConstraint( + databaseAdminClient, instanceId, databaseId)); assertTrue( "Expected to have created database " diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CustomInstanceConfigSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CustomInstanceConfigSampleIT.java similarity index 64% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/CustomInstanceConfigSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/CustomInstanceConfigSampleIT.java index 8a01d2a7a6..a56b72b75f 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/CustomInstanceConfigSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/CustomInstanceConfigSampleIT.java @@ -1,11 +1,11 @@ /* - * Copyright 2024 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; +import com.example.spanner.SampleTestBase; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -@RunWith(JUnit4.class) -public class CustomInstanceConfigSampleIT extends SampleTestBaseV2 { +public class CustomInstanceConfigSampleIT extends SampleTestBase { @Test public void testCustomInstanceConfigOperations() throws Exception { @@ -38,37 +36,26 @@ public void testCustomInstanceConfigOperations() throws Exception { projectId, instanceConfigName, customInstanceConfigId)); assertTrue(out1.contains("Created instance configuration")); - // Fetch the instance config that was created above. - final String out2 = - SampleRunner.runSample( - () -> GetInstanceConfigSample.getInstanceConfig(projectId, instanceConfigName)); - assertTrue(out2.contains("Available leader options for instance config")); - - // Fetch the instance config that was created above. - final String out3 = - SampleRunner.runSample( - () -> ListInstanceConfigsSample.listInstanceConfigs(projectId)); - assertTrue(out3.contains("Available leader options for instance config")); - // List the instance config operations. - final String out4 = + final String out2 = SampleRunner.runSample( () -> ListInstanceConfigOperationsSample.listInstanceConfigOperations(projectId)); - assertTrue(out4.contains("Obtained list of instance config operations")); + assertTrue(out2.contains("List instance config operation")); // Update display name to a randomly generated instance config id. - final String out5 = + final String out3 = SampleRunner.runSample( () -> - UpdateInstanceConfigSample.updateInstanceConfig(projectId, customInstanceConfigId)); - assertTrue(out5.contains("Updated instance configuration")); + UpdateInstanceConfigSample.updateInstanceConfig( + projectId, customInstanceConfigId)); + assertTrue(out3.contains("Updated instance configuration")); // Delete the created instance config. - final String out6 = + final String out4 = SampleRunner.runSample( () -> DeleteInstanceConfigSample.deleteInstanceConfig(projectId, customInstanceConfigId)); - assertTrue(out6.contains("Deleted instance configuration")); + assertTrue(out4.contains("Deleted instance configuration")); } } diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/DatabaseRolesIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/DatabaseRolesIT.java similarity index 82% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/DatabaseRolesIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/DatabaseRolesIT.java index 93b19a2b33..3e40189d94 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/DatabaseRolesIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/DatabaseRolesIT.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; +import com.example.spanner.SampleTestBase; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; -import com.google.common.collect.Lists; -import com.google.spanner.admin.database.v1.CreateDatabaseRequest; +import com.google.common.collect.ImmutableList; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.TimeUnit; @@ -35,24 +35,20 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** - * Integration tests for FGAC samples for GoogleStandardSql dialect. - */ +/** Integration tests for FGAC samples for GoogleStandardSql dialect. */ @RunWith(JUnit4.class) -public class DatabaseRolesIT extends SampleTestBaseV2 { +public class DatabaseRolesIT extends SampleTestBase { private static DatabaseId databaseId; @BeforeClass public static void createTestDatabase() throws Exception { final String database = idGenerator.generateDatabaseId(); - final CreateDatabaseRequest request = - CreateDatabaseRequest.newBuilder() - .setParent( - com.google.spanner.admin.database.v1.InstanceName.of(projectId, instanceId) - .toString()) - .setCreateStatement("CREATE DATABASE `" + database + "`") - .addAllExtraStatements(Lists.newArrayList( + databaseAdminClient + .createDatabase( + instanceId, + database, + ImmutableList.of( "CREATE TABLE Singers (" + " SingerId INT64 NOT NULL," + " FirstName STRING(1024)," @@ -67,8 +63,8 @@ public static void createTestDatabase() throws Exception { + " AlbumTitle STRING(MAX)," + " MarketingBudget INT64" + ") PRIMARY KEY (SingerId, AlbumId)," - + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")).build(); - databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); + + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE")) + .get(10, TimeUnit.MINUTES); databaseId = DatabaseId.of(projectId, instanceId, database); } @@ -109,7 +105,7 @@ public void testAddAndDropDatabaseRole() throws Exception { SampleRunner.runSample( () -> AddAndDropDatabaseRole.addAndDropDatabaseRole( - projectId, instanceId, databaseId.getDatabase(), "new_parent", "new_child")); + projectId, instanceId, databaseId.getDatabase(), "new-parent", "new-child")); assertTrue(out.contains("Created roles new_parent and new_child and granted privileges")); assertTrue(out.contains("Revoked privileges and dropped role new_child")); } @@ -121,6 +117,6 @@ public void testListDatabaseRoles() throws Exception { () -> ListDatabaseRoles.listDatabaseRoles( projectId, instanceId, databaseId.getDatabase())); - assertTrue(out.contains("Obtained role ")); + assertTrue(out.contains("new_parent")); } } diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/DropForeignKeyConstraintDeleteCascadeSampleIT.java similarity index 77% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/DropForeignKeyConstraintDeleteCascadeSampleIT.java index f2dae05237..a4ce31c9d8 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/DropForeignKeyConstraintDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/DropForeignKeyConstraintDeleteCascadeSampleIT.java @@ -14,31 +14,28 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; import com.example.spanner.SampleTestBase; -import com.google.common.collect.Lists; -import com.google.spanner.admin.database.v1.CreateDatabaseRequest; -import com.google.spanner.admin.database.v1.InstanceName; import java.util.Arrays; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class DropForeignKeyConstraintDeleteCascadeSampleIT extends SampleTestBaseV2 { +public class DropForeignKeyConstraintDeleteCascadeSampleIT extends SampleTestBase { @Test public void testDropForeignKeyConstraintDeleteCascade() throws Exception { // Creates database final String databaseId = idGenerator.generateDatabaseId(); - final CreateDatabaseRequest request = - CreateDatabaseRequest.newBuilder() - .setCreateStatement("CREATE DATABASE `" + databaseId + "`") - .setParent(InstanceName.of(projectId, instanceId).toString()) - .addAllExtraStatements(Lists.newArrayList( + databaseAdminClient + .createDatabase( + instanceId, + databaseId, + Arrays.asList( "CREATE TABLE Customers (\n" + " CustomerId INT64 NOT NULL,\n" + " CustomerName STRING(62) NOT NULL,\n" @@ -50,15 +47,15 @@ public void testDropForeignKeyConstraintDeleteCascade() throws Exception { + " CONSTRAINT FKShoppingCartsCustomerName" + " FOREIGN KEY (CustomerName)\n" + " REFERENCES Customers (CustomerName) ON DELETE CASCADE\n" - + " ) PRIMARY KEY (CartId)\n")).build(); - databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); + + " ) PRIMARY KEY (CartId)\n")) + .get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample( () -> DropForeignKeyConstraintDeleteCascadeSample.deleteForeignKeyDeleteCascadeConstraint( - projectId, instanceId, databaseId)); + databaseAdminClient, instanceId, databaseId)); assertTrue( "Expected to have dropped foreign-key constraints from tables in created database " 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/archived/EncryptionKeyIT.java similarity index 85% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/EncryptionKeyIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/EncryptionKeyIT.java index 5b2513d91c..ffd0771fa0 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/EncryptionKeyIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/EncryptionKeyIT.java @@ -14,18 +14,17 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static com.google.common.truth.Truth.assertThat; import com.example.spanner.SampleRunner; +import com.example.spanner.SampleTestBase; 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; @@ -35,13 +34,12 @@ import org.junit.runners.JUnit4; /** - * Integration tests for: {@link com.example.spanner.CreateDatabaseWithEncryptionKey}, - * {@link com.example.spanner.CreateBackupWithEncryptionKey} and - * {@link com.example.spanner.RestoreBackupWithEncryptionKey} + * Integration tests for: {@link CreateDatabaseWithEncryptionKey}, {@link + * CreateBackupWithEncryptionKey} and {@link RestoreBackupWithEncryptionKey} */ @RunWith(JUnit4.class) @Ignore -public class EncryptionKeyIT extends SampleTestBaseV2 { +public class EncryptionKeyIT extends SampleTestBase { private static String key; @@ -62,10 +60,16 @@ public void testEncryptedDatabaseAndBackupAndRestore() throws Exception { 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))); + CreateDatabaseWithEncryptionKey.createDatabaseWithEncryptionKey( + databaseAdminClient, + projectId, + instanceId, + databaseId, + key + )); + assertThat(out).contains( + "Database projects/" + projectId + "/instances/" + instanceId + "/databases/" + databaseId + + " created with encryption key " + key); out = SampleRunner.runSampleWithRetry(() -> CreateBackupWithEncryptionKey.createBackupWithEncryptionKey( diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/GetDatabaseDdlSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/GetDatabaseDdlSampleIT.java similarity index 56% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/GetDatabaseDdlSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/GetDatabaseDdlSampleIT.java index 8769a12598..404aceca72 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/GetDatabaseDdlSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/GetDatabaseDdlSampleIT.java @@ -14,49 +14,48 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; -import com.google.common.collect.Lists; -import com.google.spanner.admin.database.v1.CreateDatabaseRequest; -import com.google.spanner.admin.instance.v1.InstanceConfig; -import com.google.spanner.admin.instance.v1.InstanceName; +import com.example.spanner.SampleTestBase; +import com.google.cloud.spanner.InstanceConfig; +import com.google.cloud.spanner.InstanceConfigId; +import java.util.Arrays; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class GetDatabaseDdlSampleIT extends SampleTestBaseV2 { +public class GetDatabaseDdlSampleIT extends SampleTestBase { @Test public void testGetDatabaseDdl() throws Exception { // Finds a possible new leader option - final String instanceConfigId = instanceAdminClient.getInstance( - InstanceName.of(projectId, multiRegionalInstanceId)).getConfig(); - final InstanceConfig config = instanceAdminClient.getInstanceConfig(instanceConfigId); + final InstanceConfigId instanceConfigId = instanceAdminClient + .getInstance(multiRegionalInstanceId) + .getInstanceConfigId(); + final InstanceConfig config = instanceAdminClient + .getInstanceConfig(instanceConfigId.getInstanceConfig()); assertTrue( "Expected instance config " + instanceConfigId + " to have at least one leader option", - config.getLeaderOptionsList().size() > 0 + config.getLeaderOptions().size() > 0 ); - final String defaultLeader = config.getLeaderOptions(0); + final String defaultLeader = config.getLeaderOptions().get(0); // Creates database final String databaseId = idGenerator.generateDatabaseId(); - final CreateDatabaseRequest request = - CreateDatabaseRequest.newBuilder() - .setParent( - com.google.spanner.admin.database.v1.InstanceName.of(projectId, - multiRegionalInstanceId).toString()) - .setCreateStatement("CREATE DATABASE `" + databaseId + "`") - .addAllExtraStatements(Lists.newArrayList( - "CREATE TABLE Singers (Id INT64 NOT NULL) PRIMARY KEY (Id)", - "ALTER DATABASE `" - + databaseId - + "` SET OPTIONS ( default_leader = '" - + defaultLeader - + "')" - )).build(); - databaseAdminClient.createDatabaseAsync(request).get(5, TimeUnit.MINUTES); + databaseAdminClient.createDatabase( + multiRegionalInstanceId, + databaseId, + Arrays.asList( + "CREATE TABLE Singers (Id INT64 NOT NULL) PRIMARY KEY (Id)", + "ALTER DATABASE `" + + databaseId + + "` SET OPTIONS ( default_leader = '" + + defaultLeader + + "')" + ) + ).get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample(() -> GetDatabaseDdlSample diff --git a/samples/snippets/src/test/java/com/example/spanner/GetInstanceConfigSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/GetInstanceConfigSampleIT.java similarity index 90% rename from samples/snippets/src/test/java/com/example/spanner/GetInstanceConfigSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/GetInstanceConfigSampleIT.java index 2690093de8..ac3598f549 100644 --- a/samples/snippets/src/test/java/com/example/spanner/GetInstanceConfigSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/GetInstanceConfigSampleIT.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package com.example.spanner; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; +import com.example.spanner.SampleRunner; +import com.example.spanner.SampleTestBase; import org.junit.Test; public class GetInstanceConfigSampleIT extends SampleTestBase { diff --git a/samples/snippets/src/test/java/com/example/spanner/ListInstanceConfigsSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/ListInstanceConfigsSampleIT.java similarity index 89% rename from samples/snippets/src/test/java/com/example/spanner/ListInstanceConfigsSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/ListInstanceConfigsSampleIT.java index ef2b730cd3..8dcee20313 100644 --- a/samples/snippets/src/test/java/com/example/spanner/ListInstanceConfigsSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/ListInstanceConfigsSampleIT.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package com.example.spanner; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; +import com.example.spanner.SampleRunner; +import com.example.spanner.SampleTestBase; import org.junit.Test; public class ListInstanceConfigsSampleIT extends SampleTestBase { diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/PgCaseSensitivitySampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/PgCaseSensitivitySampleIT.java similarity index 62% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/PgCaseSensitivitySampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/PgCaseSensitivitySampleIT.java index 88337ba8c6..100014643d 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/PgCaseSensitivitySampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/PgCaseSensitivitySampleIT.java @@ -14,27 +14,30 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; -import com.google.spanner.admin.database.v1.CreateDatabaseRequest; -import com.google.spanner.admin.database.v1.DatabaseDialect; -import com.google.spanner.admin.database.v1.InstanceName; +import com.example.spanner.SampleTestBase; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import java.util.Collections; import org.junit.Test; -public class PgCaseSensitivitySampleIT extends SampleTestBaseV2 { +public class PgCaseSensitivitySampleIT extends SampleTestBase { @Test public void testPgCaseSensitivitySample() throws Exception { final String databaseId = idGenerator.generateDatabaseId(); - final CreateDatabaseRequest request = - CreateDatabaseRequest.newBuilder() - .setCreateStatement(getCreateDatabaseStatement(databaseId, DatabaseDialect.POSTGRESQL)) - .setParent(InstanceName.of(projectId, instanceId).toString()) - .setDatabaseDialect(DatabaseDialect.POSTGRESQL).build(); - databaseAdminClient.createDatabaseAsync(request).get(); + databaseAdminClient + .createDatabase( + databaseAdminClient + .newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId)) + .setDialect(Dialect.POSTGRESQL) + .build(), + Collections.emptyList()) + .get(); final String out = SampleRunner.runSample( diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/PgInterleavedTableSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/PgInterleavedTableSampleIT.java similarity index 61% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/PgInterleavedTableSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/PgInterleavedTableSampleIT.java index 59a0f4a524..b7d229a1b8 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/PgInterleavedTableSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/PgInterleavedTableSampleIT.java @@ -14,27 +14,30 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; -import com.google.spanner.admin.database.v1.CreateDatabaseRequest; -import com.google.spanner.admin.database.v1.DatabaseDialect; -import com.google.spanner.admin.database.v1.InstanceName; +import com.example.spanner.SampleTestBase; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import java.util.Collections; import org.junit.Test; -public class PgInterleavedTableSampleIT extends SampleTestBaseV2 { +public class PgInterleavedTableSampleIT extends SampleTestBase { @Test public void testPgInterleavedTableSample() throws Exception { final String databaseId = idGenerator.generateDatabaseId(); - final CreateDatabaseRequest request = - CreateDatabaseRequest.newBuilder() - .setCreateStatement(getCreateDatabaseStatement(databaseId, DatabaseDialect.POSTGRESQL)) - .setParent(InstanceName.of(projectId, instanceId).toString()) - .setDatabaseDialect(DatabaseDialect.POSTGRESQL).build(); - databaseAdminClient.createDatabaseAsync(request).get(); + databaseAdminClient + .createDatabase( + databaseAdminClient + .newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId)) + .setDialect(Dialect.POSTGRESQL) + .build(), + Collections.emptyList()) + .get(); final String out = SampleRunner.runSample( 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/archived/PgSpannerSampleIT.java similarity index 84% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/PgSpannerSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/PgSpannerSampleIT.java index 90885b71ce..72900b5e41 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/PgSpannerSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/PgSpannerSampleIT.java @@ -14,23 +14,22 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static com.google.common.truth.Truth.assertThat; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; -import 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.UUID; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,23 +40,26 @@ */ @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:abbreviationaswordinname") -public class PgSpannerSampleIT extends SampleTestBaseV2 { - +public class PgSpannerSampleIT { private static final int DBID_LENGTH = 20; // The instance needs to exist for tests to pass. private static final String instanceId = System.getProperty("spanner.test.instance"); private static final String baseDbId = System.getProperty("spanner.sample.database"); + private static final String databaseId = formatForTest(baseDbId); + private static final String encryptedDatabaseId = formatForTest(baseDbId); + private static final String encryptedBackupId = formatForTest(baseDbId); + private static final String encryptedRestoreId = formatForTest(baseDbId); static Spanner spanner; static DatabaseId dbId; static DatabaseAdminClient dbClient; @BeforeClass - public static void setUp() throws IOException { + public static void setUp() { SpannerOptions options = SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build(); spanner = options.getService(); - dbClient = DatabaseAdminClient.create(); - dbId = DatabaseId.of(options.getProjectId(), instanceId, idGenerator.generateDatabaseId()); + dbClient = spanner.getDatabaseAdminClient(); + dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); // Delete stale test databases that have been created earlier by this test, but not deleted. deleteStaleTestDatabases(); } @@ -66,25 +68,34 @@ 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()); + for (Database db : dbClient.listDatabases(PgSpannerSampleIT.instanceId).iterateAll()) { 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 (db.getId().getDatabase().length() >= DBID_LENGTH) { + if (samplePattern.matcher(toComparableId(PgSpannerSampleIT.baseDbId, + db.getId().getDatabase())).matches()) { + db.drop(); } - if (restoredPattern.matcher(toComparableId("restored", databaseName.getDatabase())) + if (restoredPattern.matcher(toComparableId("restored", db.getId().getDatabase())) .matches()) { - dbClient.dropDatabase(db.getName()); + db.drop(); } } } } } + @AfterClass + public static void tearDown() { + dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); + dbClient.dropDatabase( + dbId.getInstanceId().getInstance(), SpannerSample.createRestoredSampleDbId(dbId)); + dbClient.dropDatabase(instanceId, encryptedDatabaseId); + dbClient.dropDatabase(instanceId, encryptedRestoreId); + dbClient.deleteBackup(instanceId, encryptedBackupId); + spanner.close(); + } + private static String toComparableId(String baseId, String existingId) { String zeroUuid = "00000000-0000-0000-0000-0000-00000000"; int shouldBeLength = (baseId + "-" + zeroUuid).length(); @@ -98,13 +109,17 @@ private static Pattern getTestDbIdPattern(String baseDbId) { Pattern.CASE_INSENSITIVE); } - private String runSample(String command) throws Exception { + static String formatForTest(String name) { + return name + "-" + UUID.randomUUID().toString().substring(0, DBID_LENGTH); + } + + private String runSample(String command) { final PrintStream stdOut = System.out; final ByteArrayOutputStream bout = new ByteArrayOutputStream(); final PrintStream out = new PrintStream(bout); System.setOut(out); - System.out.println(instanceId + ":" + dbId.getDatabase()); - PgSpannerSample.main(new String[]{command, instanceId, dbId.getDatabase()}); + System.out.println(instanceId + ":" + databaseId); + PgSpannerSample.main(new String[]{command, instanceId, databaseId}); System.setOut(stdOut); return bout.toString(); } @@ -112,7 +127,7 @@ private String runSample(String command) throws Exception { @Test public void testSample() throws Exception { assertThat(instanceId).isNotNull(); - assertThat(dbId.getDatabase()).isNotNull(); + assertThat(databaseId).isNotNull(); System.out.println("Create Database ..."); String out = runSample("createpgdatabase"); diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/SequenceSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/SequenceSampleIT.java similarity index 69% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/SequenceSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/SequenceSampleIT.java index b3f4df004d..e1e527042a 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/SequenceSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/SequenceSampleIT.java @@ -14,15 +14,17 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static com.example.spanner.SampleRunner.runSample; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.example.spanner.SampleTestBase; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; import com.google.common.collect.ImmutableList; -import com.google.spanner.admin.database.v1.CreateDatabaseRequest; -import com.google.spanner.admin.database.v1.DatabaseDialect; +import java.util.Collections; import java.util.HashSet; import java.util.concurrent.TimeUnit; import org.junit.Before; @@ -37,23 +39,23 @@ * dialects. */ @RunWith(Parameterized.class) -public class SequenceSampleIT extends SampleTestBaseV2 { +public class SequenceSampleIT extends SampleTestBase { - private static String databaseId; + private static DatabaseId databaseId; /** * Set of dialects for which database has already been created in this test suite. This helps in * limiting the number of databases created per dialect to one. */ - private static final HashSet dbInitializedDialects = new HashSet<>(); + private static final HashSet dbInitializedDialects = new HashSet<>(); @Parameters(name = "dialect = {0}") - public static Iterable data() { - return ImmutableList.of(DatabaseDialect.GOOGLE_STANDARD_SQL, DatabaseDialect.POSTGRESQL); + public static Iterable data() { + return ImmutableList.of(Dialect.GOOGLE_STANDARD_SQL, Dialect.POSTGRESQL); } @Parameter(0) - public static DatabaseDialect dialect; + public static Dialect dialect; @Before public void createTestDatabase() throws Exception { @@ -62,36 +64,37 @@ public void createTestDatabase() throws Exception { return; } dbInitializedDialects.add(dialect); - databaseId = idGenerator.generateDatabaseId(); - CreateDatabaseRequest createDatabaseRequest = - CreateDatabaseRequest.newBuilder() - .setParent(getInstanceName(projectId, instanceId)) - .setCreateStatement(getCreateDatabaseStatement(databaseId, dialect)) - .setDatabaseDialect(dialect).build(); + final String database = idGenerator.generateDatabaseId(); databaseAdminClient - .createDatabaseAsync(createDatabaseRequest) + .createDatabase( + databaseAdminClient + .newDatabaseBuilder(DatabaseId.of(projectId, instanceId, database)) + .setDialect(dialect) + .build(), + Collections.emptyList()) .get(10, TimeUnit.MINUTES); + databaseId = DatabaseId.of(projectId, instanceId, database); } @Test public void createSequence() throws Exception { String out; - if (dialect == DatabaseDialect.GOOGLE_STANDARD_SQL) { + if (dialect == Dialect.GOOGLE_STANDARD_SQL) { out = runSample( () -> CreateSequenceSample.createSequence( - projectId, instanceId, databaseId)); + projectId, instanceId, databaseId.getDatabase())); } else { out = runSample( () -> PgCreateSequenceSample.pgCreateSequence( - projectId, instanceId, databaseId)); + projectId, instanceId, databaseId.getDatabase())); } assertTrue( out.contains( - "Created Seq sequence and Customers table, where the key column " + "Created Seq sequence and Customers table, where its key column " + "CustomerId uses the sequence as a default value")); assertEquals(out.split("Inserted customer record with CustomerId", -1).length - 1, 3); assertTrue(out.contains("Number of customer records inserted is: 3")); @@ -100,18 +103,18 @@ public void createSequence() throws Exception { @Test public void alterSequence() throws Exception { String out; - if (dialect == DatabaseDialect.GOOGLE_STANDARD_SQL) { + if (dialect == Dialect.GOOGLE_STANDARD_SQL) { out = runSample( () -> AlterSequenceSample.alterSequence( - projectId, instanceId, databaseId)); + projectId, instanceId, databaseId.getDatabase())); } else { out = runSample( () -> PgAlterSequenceSample.pgAlterSequence( - projectId, instanceId, databaseId)); + projectId, instanceId, databaseId.getDatabase())); } assertTrue( out.contains("Altered Seq sequence to skip an inclusive range between 1000 and 5000000")); @@ -122,17 +125,17 @@ public void alterSequence() throws Exception { @Test public void dropSequence() throws Exception { String out; - if (dialect == DatabaseDialect.GOOGLE_STANDARD_SQL) { + if (dialect == Dialect.GOOGLE_STANDARD_SQL) { out = runSample( () -> - DropSequenceSample.dropSequence(projectId, instanceId, databaseId)); + DropSequenceSample.dropSequence(projectId, instanceId, databaseId.getDatabase())); } else { out = runSample( () -> PgDropSequenceSample.pgDropSequence( - projectId, instanceId, databaseId)); + projectId, instanceId, databaseId.getDatabase())); } assertTrue( out.contains( diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/archived/SpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/SpannerSampleIT.java new file mode 100644 index 0000000000..66410cce4d --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/SpannerSampleIT.java @@ -0,0 +1,593 @@ +/* + * 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.archived; + +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.Backup; +import com.google.cloud.spanner.BackupId; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +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.common.base.Preconditions; +import com.google.common.util.concurrent.Uninterruptibles; +import java.io.ByteArrayOutputStream; +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.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.threeten.bp.LocalDate; +import org.threeten.bp.temporal.ChronoField; + +/** Unit tests for {@code SpannerSample} */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:abbreviationaswordinname") +public class SpannerSampleIT { + 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 databaseId = formatForTest(baseDbId); + private static final String encryptedDatabaseId = formatForTest(baseDbId); + private static final String encryptedBackupId = formatForTest(baseDbId); + private static final String encryptedRestoreId = formatForTest(baseDbId); + private static final long STALE_INSTANCE_THRESHOLD_SECS = + TimeUnit.SECONDS.convert(24L, TimeUnit.HOURS); + static Spanner spanner; + static DatabaseId dbId; + static DatabaseAdminClient dbClient; + private static String key; + private long lastUpdateDataTimeInMillis; + + private String runSample(String command) 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}); + System.setOut(stdOut); + return bout.toString(); + } + + @BeforeClass + public static void setUp() throws Exception { + SpannerOptions options = + SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build(); + spanner = options.getService(); + dbClient = spanner.getDatabaseAdminClient(); + dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); + // Delete stale test databases that have been created earlier by this test, but not deleted. + deleteStaleTestDatabases(instanceId, baseDbId); + 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(String instanceId, String baseDbId) { + Timestamp now = Timestamp.now(); + Pattern samplePattern = getTestDbIdPattern(baseDbId); + Pattern restoredPattern = getTestDbIdPattern("restored"); + for (Database db : dbClient.listDatabases(instanceId).iterateAll()) { + if (TimeUnit.HOURS.convert(now.getSeconds() - db.getCreateTime().getSeconds(), + TimeUnit.SECONDS) > 24) { + if (db.getId().getDatabase().length() >= DBID_LENGTH) { + if (samplePattern.matcher(toComparableId(baseDbId, db.getId().getDatabase())).matches()) { + db.drop(); + } + if (restoredPattern.matcher(toComparableId("restored", db.getId().getDatabase())) + .matches()) { + db.drop(); + } + } + } + } + } + + @AfterClass + public static void tearDown() throws Exception { + dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); + dbClient.dropDatabase( + dbId.getInstanceId().getInstance(), SpannerSample.createRestoredSampleDbId(dbId)); + dbClient.dropDatabase(instanceId, encryptedDatabaseId); + dbClient.dropDatabase(instanceId, encryptedRestoreId); + dbClient.deleteBackup(instanceId, encryptedBackupId); + spanner.close(); + } + + @Test + public void testSample() throws Exception { + assertThat(instanceId).isNotNull(); + assertThat(databaseId).isNotNull(); + String out = runSample("createdatabase"); + assertThat(out).contains("Created database"); + assertThat(out).contains(dbId.getName()); + + runSample("write"); + + out = runSample("delete"); + assertThat(out).contains("Records deleted."); + + runSample("write"); + + out = runSample("read"); + assertThat(out).contains("1 1 Total Junk"); + + out = runSample("query"); + assertThat(out).contains("1 1 Total Junk"); + runSample("addmarketingbudget"); + + // 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"); + out = runSample("readstaledata"); + assertThat(out).contains("1 1 NULL"); + runSample("writetransaction"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 300000"); + assertThat(out).contains("2 2 300000"); + + runSample("addindex"); + out = runSample("queryindex"); + assertThat(out).contains("Go, Go, Go"); + assertThat(out).contains("Forever Hold Your Peace"); + assertThat(out).doesNotContain("Green"); + + out = runSample("readindex"); + assertThat(out).contains("Go, Go, Go"); + assertThat(out).contains("Forever Hold Your Peace"); + assertThat(out).contains("Green"); + + runSample("addstoringindex"); + out = runSample("readstoringindex"); + assertThat(out).contains("300000"); + + out = runSample("readonlytransaction"); + assertThat(out.replaceAll("[\r\n]+", " ")).containsMatch("(Total Junk.*){2}"); + + out = runSample("addcommittimestamp"); + assertThat(out).contains("Added LastUpdateTime as a commit timestamp column"); + + runSample("updatewithtimestamp"); + out = runSample("querywithtimestamp"); + assertThat(out).contains("1 1 1000000"); + assertThat(out).contains("2 2 750000"); + + out = runSample("createtablewithtimestamp"); + assertThat(out).contains("Created Performances table in database"); + + runSample("writewithtimestamp"); + out = runSample("queryperformancestable"); + 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"); + out = runSample("querywithstruct"); + assertThat(out).startsWith("6\n"); + + out = runSample("querywitharrayofstruct"); + assertThat(out).startsWith("8\n7\n6"); + + out = runSample("querystructfield"); + assertThat(out).startsWith("6\n"); + + out = runSample("querynestedstructfield"); + assertThat(out).contains("6 Imagination\n"); + assertThat(out).contains("9 Imagination\n"); + + runSample("insertusingdml"); + out = runSample("querysingerstable"); + assertThat(out).contains("Virginia Watson"); + + runSample("updateusingdml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 2000000"); + + runSample("deleteusingdml"); + out = runSample("querysingerstable"); + assertThat(out).doesNotContain("Alice Trentor"); + + out = runSample("updateusingdmlwithtimestamp"); + assertThat(out).contains("2 records updated"); + + out = runSample("writeandreadusingdml"); + assertThat(out).contains("Timothy Campbell"); + + runSample("updateusingdmlwithstruct"); + out = runSample("querysingerstable"); + assertThat(out).contains("Timothy Grant"); + + runSample("writeusingdml"); + out = runSample("querysingerstable"); + assertThat(out).contains("Melissa Garcia"); + assertThat(out).contains("Russell Morales"); + assertThat(out).contains("Jacqueline Long"); + assertThat(out).contains("Dylan Shaw"); + out = runSample("querywithparameter"); + assertThat(out).contains("12 Melissa Garcia"); + + runSample("writewithtransactionusingdml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 2200000"); + assertThat(out).contains("2 2 550000"); + + runSample("updateusingpartitioneddml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 2200000"); + assertThat(out).contains("2 2 100000"); + + 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"); + + out = runSample("updateusingbatchdml"); + assertThat(out).contains("1 record updated by stmt 0"); + assertThat(out).contains("1 record updated by stmt 1"); + + out = runSample("createtablewithdatatypes"); + assertThat(out).contains("Created Venues table in database"); + + runSample("writedatatypesdata"); + out = runSample("querywitharray"); + assertThat(out).contains("19 Venue 19 2020-11-01"); + assertThat(out).contains("42 Venue 42 2020-10-01"); + + out = runSample("querywithbool"); + assertThat(out).contains("19 Venue 19 true"); + + out = runSample("querywithbytes"); + assertThat(out).contains("4 Venue 4"); + + out = runSample("querywithdate"); + assertThat(out).contains("4 Venue 4 2018-09-02"); + assertThat(out).contains("42 Venue 42 2018-10-01"); + + out = runSample("querywithfloat"); + assertThat(out).contains("4 Venue 4 0.8"); + assertThat(out).contains("19 Venue 19 0.9"); + + out = runSample("querywithint"); + assertThat(out).contains("19 Venue 19 6300"); + assertThat(out).contains("42 Venue 42 3000"); + + out = runSample("querywithstring"); + assertThat(out).contains("42 Venue 42"); + + out = runSample("querywithtimestampparameter"); + assertThat(out).contains("4 Venue 4"); + assertThat(out).contains("19 Venue 19"); + assertThat(out).contains("42 Venue 42"); + + out = runSample("querywithnumeric"); + assertThat(out).contains("19 Venue 19 1200100"); + assertThat(out).contains("42 Venue 42 390650.99"); + + out = runSample("clientwithqueryoptions"); + assertThat(out).contains("1 1 Total Junk"); + out = runSample("querywithqueryoptions"); + assertThat(out).contains("1 1 Total Junk"); + + String backupName = + String.format( + "%s_%02d", + dbId.getDatabase(), LocalDate.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR)); + BackupId backupId = BackupId.of(dbId.getInstanceId(), backupName); + + out = runSample("createbackup"); + assertThat(out).contains("Created backup [" + backupId + "]"); + + out = runSample("cancelcreatebackup"); + assertThat(out).contains( + "Backup operation for [" + backupId + "_cancel] successfully"); + + // TODO: remove try-catch when filtering on metadata fields works. + try { + out = runSample("listbackupoperations"); + assertThat(out).contains( + String.format( + "Backup %s on database %s pending:", + backupId.getName(), + dbId.getName())); + assertTrue("Out does not contain copy backup operations", out.contains( + "Copy Backup Operations")); + } catch (SpannerException e) { + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.INVALID_ARGUMENT); + assertThat(e.getMessage()).contains("Cannot evaluate filter expression"); + } + + out = runSample("listbackups"); + assertThat(out).contains("All backups:"); + assertThat(out).contains( + String.format("All backups with backup name containing \"%s\":", backupId.getBackup())); + 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 7 times. + assertThat(countOccurrences(out, backupId.getName())).isEqualTo(7); + + // 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 { + out = runSample("restorebackup"); + assertThat(out).contains( + "Restored database [" + + dbId.getName() + + "] from [" + + backupId.getName() + + "]"); + 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) { + out = runSample("listdatabaseoperations"); + assertThat(out).contains( + String.format( + "Database %s restored from backup", + DatabaseId.of( + dbId.getInstanceId(), + SpannerSample.createRestoredSampleDbId(dbId)) + .getName())); + } + + out = runSample("updatebackup"); + 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. + dbClient.dropDatabase( + dbId.getInstanceId().getInstance(), SpannerSample.createRestoredSampleDbId(dbId)); + + out = runSample("deletebackup"); + assertThat(out).contains("Deleted backup [" + backupId + "]"); + } + + @Test + public void testEncryptedDatabaseAndBackupSamples() throws Exception { + String projectId = spanner.getOptions().getProjectId(); + // 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 = String.format("encrypted-test-%s", UUID.randomUUID()); + 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(() -> CreateDatabaseWithEncryptionKey.createDatabaseWithEncryptionKey(dbClient, + projectId, instanceId, encryptedDatabaseId, key)); + assertThat(out).contains(String.format( + "Database projects/%s/instances/%s/databases/%s created with encryption key %s", + projectId, instanceId, encryptedDatabaseId, key)); + + out = SampleRunner.runSampleWithRetry( + () -> CreateBackupWithEncryptionKey.createBackupWithEncryptionKey(dbClient, projectId, + instanceId, encryptedDatabaseId, 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(dbClient, projectId, + instanceId, encryptedBackupId, encryptedRestoreId, 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, encryptedDatabaseId, projectId, instanceId, encryptedRestoreId, + 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); + } + } + + private static void deleteAllBackups(String instanceId) throws InterruptedException { + for (Backup backup : dbClient.listBackups(instanceId).iterateAll()) { + int attempts = 0; + while (attempts < 30) { + try { + attempts++; + backup.delete(); + 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 instanceId = formatForTest("sample-inst"); + String out = + runSampleRunnable(() -> { + try { + CreateInstanceExample.createInstance( + dbId.getInstanceId().getProject(), instanceId); + } 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; + } + } +} diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/UpdateDatabaseSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/UpdateDatabaseSampleIT.java similarity index 53% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/UpdateDatabaseSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/UpdateDatabaseSampleIT.java index acac98cde4..29a99ed3b8 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/UpdateDatabaseSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/UpdateDatabaseSampleIT.java @@ -14,58 +14,49 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; +import com.example.spanner.SampleTestBase; +import com.example.spanner.admin.archived.UpdateDatabaseSample; import com.google.api.gax.longrunning.OperationFuture; -import com.google.common.collect.Lists; -import com.google.protobuf.FieldMask; -import com.google.spanner.admin.database.v1.Database; -import com.google.spanner.admin.database.v1.DatabaseName; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.DatabaseInfo.DatabaseField; import com.google.spanner.admin.database.v1.UpdateDatabaseMetadata; -import com.google.spanner.admin.database.v1.UpdateDatabaseRequest; +import java.util.Collections; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class UpdateDatabaseSampleIT extends SampleTestBaseV2 { +public class UpdateDatabaseSampleIT extends SampleTestBase { @Test public void testUpdateDatabase() throws Exception { // Create database final String databaseId = idGenerator.generateDatabaseId(); databaseAdminClient - .createDatabaseAsync(getInstanceName(projectId, instanceId), - "CREATE DATABASE `" + databaseId + "`") + .createDatabase(instanceId, databaseId, Collections.emptyList()) .get(5, TimeUnit.MINUTES); // Runs sample final String out = SampleRunner.runSample( () -> UpdateDatabaseSample.updateDatabase(projectId, instanceId, databaseId)); + + DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId); assertTrue( "Expected that database would have been updated. Output received was " + out, - out.contains(String.format( - "Updated database %s", DatabaseName.of(projectId, instanceId, databaseId)))); + out.contains(String.format("Updated database %s", dbId))); // Cleanup - final com.google.spanner.admin.database.v1.Database database = - com.google.spanner.admin.database.v1.Database.newBuilder() - .setName(DatabaseName.of(projectId, instanceId, databaseId).toString()) - .setEnableDropProtection(false).build(); - final UpdateDatabaseRequest updateDatabaseRequest = - UpdateDatabaseRequest.newBuilder() - .setDatabase(database) - .setUpdateMask( - FieldMask.newBuilder().addAllPaths( - Lists.newArrayList("enable_drop_protection")).build()) - .build(); - + Database databaseToUpdate = + databaseAdminClient.newDatabaseBuilder(dbId).disableDropProtection().build(); OperationFuture operation = - databaseAdminClient.updateDatabaseAsync(updateDatabaseRequest); + databaseAdminClient.updateDatabase(databaseToUpdate, DatabaseField.DROP_PROTECTION); Database updatedDb = operation.get(5, TimeUnit.MINUTES); - assertFalse(updatedDb.getEnableDropProtection()); + assertFalse(updatedDb.isDropProtectionEnabled()); } } diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/archived/UpdateDatabaseWithDefaultLeaderSampleIT.java similarity index 61% rename from samples/snippets/src/test/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSampleIT.java rename to samples/snippets/src/test/java/com/example/spanner/admin/archived/UpdateDatabaseWithDefaultLeaderSampleIT.java index 3ec3587288..ce657575f0 100644 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/UpdateDatabaseWithDefaultLeaderSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/admin/archived/UpdateDatabaseWithDefaultLeaderSampleIT.java @@ -14,40 +14,45 @@ * limitations under the License. */ -package com.example.spanner.admin.generated; +package com.example.spanner.admin.archived; import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; -import com.google.spanner.admin.database.v1.Database; -import com.google.spanner.admin.instance.v1.InstanceConfig; +import com.example.spanner.SampleTestBase; +import com.example.spanner.admin.archived.UpdateDatabaseWithDefaultLeaderSample; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.InstanceConfig; +import com.google.cloud.spanner.InstanceConfigId; +import java.util.Collections; import java.util.concurrent.TimeUnit; import org.junit.Test; -public class UpdateDatabaseWithDefaultLeaderSampleIT extends SampleTestBaseV2 { +public class UpdateDatabaseWithDefaultLeaderSampleIT extends SampleTestBase { @Test public void testUpdateDatabaseWithDefaultLeader() throws Exception { // Create database final String databaseId = idGenerator.generateDatabaseId(); final Database createdDatabase = databaseAdminClient - .createDatabaseAsync(getInstanceName(projectId, multiRegionalInstanceId), - "CREATE DATABASE `" + databaseId + "`") + .createDatabase(multiRegionalInstanceId, databaseId, Collections.emptyList()) .get(5, TimeUnit.MINUTES); final String defaultLeader = createdDatabase.getDefaultLeader(); // Finds a possible new leader option - final String instanceConfigId = - instanceAdminClient.getInstance(getInstanceName(projectId, multiRegionalInstanceId)) - .getConfig(); - final InstanceConfig config = instanceAdminClient.getInstanceConfig(instanceConfigId); - final String newLeader = - config.getLeaderOptionsList().stream() - .filter(leader -> !leader.equals(defaultLeader)) - .findFirst().orElseThrow(() -> - new RuntimeException("Expected to find a leader option different than " - + defaultLeader) - ); + final InstanceConfigId instanceConfigId = instanceAdminClient + .getInstance(multiRegionalInstanceId) + .getInstanceConfigId(); + final InstanceConfig config = instanceAdminClient + .getInstanceConfig(instanceConfigId.getInstanceConfig()); + final String newLeader = config + .getLeaderOptions() + .stream() + .filter(leader -> !leader.equals(defaultLeader)) + .findFirst() + .orElseThrow(() -> + new RuntimeException("Expected to find a leader option different than " + defaultLeader) + ); // Runs sample final String out = SampleRunner.runSample(() -> UpdateDatabaseWithDefaultLeaderSample 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 deleted file mode 100644 index cf719ff9e4..0000000000 --- a/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java +++ /dev/null @@ -1,694 +0,0 @@ -/* - * 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 66ddf6c1dd2a2b944ca06ee7bbce994fb87bd3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Mon, 4 Mar 2024 10:38:15 +0100 Subject: [PATCH 12/16] test: support float32 in mock server (#2937) Adds support for `float32` to the mock server for testing. Also adds a couple of tests that can be used to verify that the mock server supports all types, and that verifies that the client library reads and encodes all types correctly. These tests can be overridden by frameworks using the Java client (e.g. JDBC and PGAdapter) to verify that these also handle these types correctly when used through the API of that tool (I.e. through JDBC or PostgreSQL wire-protocol). --- .../cloud/spanner/MockSpannerServiceImpl.java | 13 + .../connection/AllTypesMockServerTest.java | 683 ++++++++++++++++++ 2 files changed, 696 insertions(+) create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AllTypesMockServerTest.java diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java index 3b0b7812d2..7073ebd28e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java @@ -1286,6 +1286,7 @@ private Statement buildStatement( break; case FLOAT32: builder.bind(fieldName).toFloat32Array((Iterable) null); + break; case FLOAT64: builder.bind(fieldName).toFloat64Array((Iterable) null); break; @@ -1331,6 +1332,7 @@ private Statement buildStatement( break; case FLOAT32: builder.bind(fieldName).to((Float) null); + break; case FLOAT64: builder.bind(fieldName).to((Double) null); break; @@ -1396,6 +1398,14 @@ private Statement buildStatement( GrpcStruct.decodeArrayValue( com.google.cloud.spanner.Type.date(), value.getListValue())); break; + case FLOAT32: + builder + .bind(fieldName) + .toFloat32Array( + (Iterable) + GrpcStruct.decodeArrayValue( + com.google.cloud.spanner.Type.float32(), value.getListValue())); + break; case FLOAT64: builder .bind(fieldName) @@ -1479,6 +1489,9 @@ private Statement buildStatement( case DATE: builder.bind(fieldName).to(Date.parseDate(value.getStringValue())); break; + case FLOAT32: + builder.bind(fieldName).to((float) value.getNumberValue()); + break; case FLOAT64: builder.bind(fieldName).to(value.getNumberValue()); break; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AllTypesMockServerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AllTypesMockServerTest.java new file mode 100644 index 0000000000..44ae730f8c --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/AllTypesMockServerTest.java @@ -0,0 +1,683 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.connection; + +import static org.junit.Assert.*; + +import com.google.cloud.ByteArray; +import com.google.cloud.Date; +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.SingerProto.Genre; +import com.google.cloud.spanner.SingerProto.SingerInfo; +import com.google.cloud.spanner.Statement; +import com.google.common.collect.ImmutableList; +import com.google.protobuf.ListValue; +import com.google.protobuf.NullValue; +import com.google.protobuf.Value; +import com.google.spanner.v1.ExecuteSqlRequest; +import com.google.spanner.v1.Type; +import com.google.spanner.v1.TypeCode; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +/** Tests that all types can be read from Spanner and sent to Spanner. */ +@RunWith(Parameterized.class) +public class AllTypesMockServerTest extends AbstractMockServerTest { + + @Parameters(name = "dialect = {0}") + public static Object[] data() { + return Dialect.values(); + } + + @Parameter public Dialect dialect; + + private Dialect currentDialect; + + public static final Statement SELECT_STATEMENT = Statement.of("select * from all_types"); + + public static final boolean BOOL_VALUE = true; + public static final long INT64_VALUE = 1L; + public static final float FLOAT32_VALUE = 3.14f; + public static final double FLOAT64_VALUE = 3.14d; + public static final BigDecimal NUMERIC_VALUE = new BigDecimal("3.14"); + public static final String PG_NUMERIC_VALUE = "3.14"; + public static final String STRING_VALUE = "test-string"; + public static final String JSON_VALUE = "{\"key1\":\"value1\", \"key2\":\"value2\"}"; + public static final byte[] BYTES_VALUE = "test-bytes".getBytes(StandardCharsets.UTF_8); + public static final Date DATE_VALUE = Date.fromYearMonthDay(2024, 3, 2); + public static final Timestamp TIMESTAMP_VALUE = + Timestamp.parseTimestamp("2024-03-02T07:07:00.20982735Z"); + + public static final List BOOL_ARRAY_VALUE = Arrays.asList(true, null, false); + public static final List INT64_ARRAY_VALUE = + Arrays.asList(100L, null, 200L, Long.MIN_VALUE, Long.MAX_VALUE); + public static final List FLOAT32_ARRAY_VALUE = + Arrays.asList( + -3.14f, null, 6.626f, Float.MIN_VALUE, Float.MAX_VALUE, Float.MIN_NORMAL, Float.NaN); + public static final List FLOAT64_ARRAY_VALUE = + Arrays.asList( + -3.14d, null, 6.626d, Double.MIN_VALUE, Double.MAX_VALUE, Double.MIN_NORMAL, Double.NaN); + public static final List NUMERIC_ARRAY_VALUE = + Arrays.asList( + new BigDecimal("-3.14"), + null, + new BigDecimal("99.99"), + BigDecimal.ZERO, + new BigDecimal("1e-9"), + new BigDecimal("-9.9999999999999999999999999999999999999E+28"), + new BigDecimal("9.9999999999999999999999999999999999999E+28")); + public static final List PG_NUMERIC_ARRAY_VALUE = + Arrays.asList( + "-3.14", + null, + "99.99", + "NaN", + "1e-9", + "-9.9999999999999999999999999999999999999E+28", + "9.9999999999999999999999999999999999999E+28"); + public static final List STRING_ARRAY_VALUE = + Arrays.asList("test-string1", null, "test-string2"); + public static final List JSON_ARRAY_VALUE = + Arrays.asList( + "{\"key1\":\"value1.1\", \"key2\":\"value1.2\"}", + null, + "{\"key1\":\"value3.1\", \"key2\":\"value3.2\"}"); + public static final List BYTES_ARRAY_VALUE = + Arrays.asList(ByteArray.copyFrom("test-bytes1"), null, ByteArray.copyFrom("test-bytes2")); + public static final List DATE_ARRAY_VALUE = + Arrays.asList( + Date.fromYearMonthDay(2024, 3, 1), + null, + Date.fromYearMonthDay(2024, 3, 3), + Date.fromYearMonthDay(1, 1, 1), + Date.fromYearMonthDay(9999, 12, 31)); + public static final List TIMESTAMP_ARRAY_VALUE = + Arrays.asList( + Timestamp.parseTimestamp("2024-03-01T07:07:00.20982735Z"), + null, + Timestamp.parseTimestamp("2024-03-03T07:07:00Z"), + Timestamp.MIN_VALUE, + Timestamp.MAX_VALUE); + + @Before + public void setupDialect() { + if (currentDialect != dialect) { + mockSpanner.putStatementResult(StatementResult.detectDialectResult(dialect)); + setupAllTypesResultSet(dialect); + mockSpanner.putStatementResult(StatementResult.update(createInsertStatement(dialect), 1L)); + SpannerPool.closeSpannerPool(); + currentDialect = dialect; + } + } + + private void setupAllTypesResultSet(Dialect dialect) { + // Use RandomResultSetGenerator to generate metadata for a ResultSet with all types. + // This guarantees that this test will fail if a new type is added to RandomResultSetGenerator, + // but not added to this test. + // The columns in the result set are: + // COL1: BOOL + // COL2: INT64 + // COL3: FLOAT32 + // COL4: FLOAT64 + // COL5: NUMERIC / PG_NUMERIC + // COL6: STRING + // COL7: JSON / PG_JSONB + // COL8: BYTES + // COL9: DATE + // COL10: TIMESTAMP + // COL11-20: ARRAY<..> for the types above. + // Only for GoogleSQL: + // COL21: PROTO + // COL22: ENUM + // COL23: ARRAY + // COL24: ARRAY + ListValue.Builder row1Builder = + ListValue.newBuilder() + .addValues(Value.newBuilder().setBoolValue(BOOL_VALUE)) + .addValues(Value.newBuilder().setStringValue(String.valueOf(INT64_VALUE)).build()) + .addValues(Value.newBuilder().setNumberValue(FLOAT32_VALUE)) + .addValues(Value.newBuilder().setNumberValue(FLOAT64_VALUE)) + .addValues( + Value.newBuilder() + .setStringValue( + dialect == Dialect.POSTGRESQL + ? PG_NUMERIC_VALUE + : NUMERIC_VALUE.toEngineeringString())) + .addValues(Value.newBuilder().setStringValue(STRING_VALUE)) + .addValues(Value.newBuilder().setStringValue(JSON_VALUE)) + .addValues( + Value.newBuilder().setStringValue(Base64.getEncoder().encodeToString(BYTES_VALUE))) + .addValues(Value.newBuilder().setStringValue(DATE_VALUE.toString())) + .addValues(Value.newBuilder().setStringValue(TIMESTAMP_VALUE.toString())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + BOOL_ARRAY_VALUE.stream() + .map( + b -> + b == null + ? Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build() + : Value.newBuilder().setBoolValue(b).build()) + .collect(Collectors.toList())) + .build())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + INT64_ARRAY_VALUE.stream() + .map( + l -> + l == null + ? Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build() + : Value.newBuilder() + .setStringValue(String.valueOf(l)) + .build()) + .collect(Collectors.toList())) + .build())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + FLOAT32_ARRAY_VALUE.stream() + .map( + f -> { + if (f == null) { + return Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build(); + } else if (Float.isNaN(f)) { + return Value.newBuilder().setStringValue("NaN").build(); + } else { + return Value.newBuilder().setNumberValue(f).build(); + } + }) + .collect(Collectors.toList())) + .build())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + FLOAT64_ARRAY_VALUE.stream() + .map( + d -> { + if (d == null) { + return Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build(); + } else if (Double.isNaN(d)) { + return Value.newBuilder().setStringValue("NaN").build(); + } else { + return Value.newBuilder().setNumberValue(d).build(); + } + }) + .collect(Collectors.toList())) + .build())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + dialect == Dialect.POSTGRESQL + ? PG_NUMERIC_ARRAY_VALUE.stream() + .map( + string -> + string == null + ? Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build() + : Value.newBuilder() + .setStringValue(string) + .build()) + .collect(Collectors.toList()) + : NUMERIC_ARRAY_VALUE.stream() + .map( + bigDecimal -> + bigDecimal == null + ? Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build() + : Value.newBuilder() + .setStringValue( + bigDecimal.toEngineeringString()) + .build()) + .collect(Collectors.toList())) + .build())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + STRING_ARRAY_VALUE.stream() + .map( + string -> + string == null + ? Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build() + : Value.newBuilder().setStringValue(string).build()) + .collect(Collectors.toList())) + .build())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + JSON_ARRAY_VALUE.stream() + .map( + json -> + json == null + ? Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build() + : Value.newBuilder().setStringValue(json).build()) + .collect(Collectors.toList())) + .build())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + BYTES_ARRAY_VALUE.stream() + .map( + byteArray -> + byteArray == null + ? Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build() + : Value.newBuilder() + .setStringValue( + Base64.getEncoder() + .encodeToString( + byteArray.toByteArray())) + .build()) + .collect(Collectors.toList())) + .build())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + DATE_ARRAY_VALUE.stream() + .map( + date -> + date == null + ? Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build() + : Value.newBuilder() + .setStringValue(date.toString()) + .build()) + .collect(Collectors.toList())) + .build())) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addAllValues( + TIMESTAMP_ARRAY_VALUE.stream() + .map( + timestamp -> + timestamp == null + ? Value.newBuilder() + .setNullValue(NullValue.NULL_VALUE) + .build() + : Value.newBuilder() + .setStringValue(timestamp.toString()) + .build()) + .collect(Collectors.toList())) + .build())); + + if (dialect == Dialect.GOOGLE_STANDARD_SQL) { + // Add PROTO values. + row1Builder + .addValues( + Value.newBuilder() + .setStringValue( + Base64.getEncoder() + .encodeToString( + SingerInfo.newBuilder() + .setSingerId(1L) + .setNationality("unknown") + .setBirthDate("1986-09-30") + .setGenre(Genre.POP) + .build() + .toByteArray())) + .build()) + .addValues(Value.newBuilder().setStringValue(String.valueOf(Genre.JAZZ_VALUE)).build()) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + Value.newBuilder() + .setStringValue( + Base64.getEncoder() + .encodeToString( + SingerInfo.newBuilder() + .setSingerId(1L) + .setGenre(Genre.FOLK) + .setBirthDate("200-01-01") + .setNationality("no") + .build() + .toByteArray())) + .build()) + .addValues(Value.newBuilder().setNullValue(NullValue.NULL_VALUE)) + .addValues( + Value.newBuilder() + .setStringValue( + Base64.getEncoder() + .encodeToString( + SingerInfo.newBuilder() + .setSingerId(2L) + .setGenre(Genre.JAZZ) + .setBirthDate("200-01-02") + .setNationality("dk") + .build() + .toByteArray())) + .build()) + .build()) + .build()) + .addValues( + Value.newBuilder() + .setListValue( + ListValue.newBuilder() + .addValues( + Value.newBuilder() + .setStringValue(String.valueOf(Genre.ROCK_VALUE)) + .build()) + .addValues(Value.newBuilder().setNullValue(NullValue.NULL_VALUE)) + .addValues( + Value.newBuilder() + .setStringValue(String.valueOf(Genre.ROCK_VALUE)) + .build()) + .build()) + .build()); + } + + com.google.spanner.v1.ResultSet resultSet = + com.google.spanner.v1.ResultSet.newBuilder() + .setMetadata( + RandomResultSetGenerator.generateAllTypesMetadata( + RandomResultSetGenerator.generateAllTypes(dialect))) + .addRows(row1Builder.build()) + .build(); + mockSpanner.putStatementResults(StatementResult.query(SELECT_STATEMENT, resultSet)); + } + + public static Statement createInsertStatement(Dialect dialect) { + Statement.Builder builder = Statement.newBuilder("insert into all_types ("); + builder.append( + IntStream.rangeClosed(1, RandomResultSetGenerator.generateAllTypes(dialect).length) + .mapToObj(col -> "COL" + col) + .collect(Collectors.joining(", ", "", ") values ("))); + builder.append( + IntStream.rangeClosed(1, RandomResultSetGenerator.generateAllTypes(dialect).length) + .mapToObj(col -> "@p" + col) + .collect(Collectors.joining(", ", "", ")"))); + int param = 0; + return builder + .bind("p" + ++param) + .to(BOOL_VALUE) + .bind("p" + ++param) + .to(INT64_VALUE) + .bind("p" + ++param) + .to(FLOAT32_VALUE) + .bind("p" + ++param) + .to(FLOAT64_VALUE) + .bind("p" + ++param) + .to( + dialect == Dialect.POSTGRESQL + ? com.google.cloud.spanner.Value.pgNumeric(PG_NUMERIC_VALUE) + : com.google.cloud.spanner.Value.numeric(NUMERIC_VALUE)) + .bind("p" + ++param) + .to(STRING_VALUE) + .bind("p" + ++param) + .to( + dialect == Dialect.POSTGRESQL + ? com.google.cloud.spanner.Value.pgJsonb(JSON_VALUE) + : com.google.cloud.spanner.Value.json(JSON_VALUE)) + .bind("p" + ++param) + .to(ByteArray.copyFrom(BYTES_VALUE)) + .bind("p" + ++param) + .to(DATE_VALUE) + .bind("p" + ++param) + .to(TIMESTAMP_VALUE) + .bind("p" + ++param) + .toBoolArray(BOOL_ARRAY_VALUE) + .bind("p" + ++param) + .toInt64Array(INT64_ARRAY_VALUE) + .bind("p" + ++param) + .toFloat32Array(FLOAT32_ARRAY_VALUE) + .bind("p" + ++param) + .toFloat64Array(FLOAT64_ARRAY_VALUE) + .bind("p" + ++param) + .to( + dialect == Dialect.POSTGRESQL + ? com.google.cloud.spanner.Value.pgNumericArray(PG_NUMERIC_ARRAY_VALUE) + : com.google.cloud.spanner.Value.numericArray(NUMERIC_ARRAY_VALUE)) + .bind("p" + ++param) + .toStringArray(STRING_ARRAY_VALUE) + .bind("p" + ++param) + .to( + dialect == Dialect.POSTGRESQL + ? com.google.cloud.spanner.Value.pgJsonbArray(JSON_ARRAY_VALUE) + : com.google.cloud.spanner.Value.jsonArray(JSON_ARRAY_VALUE)) + .bind("p" + ++param) + .toBytesArray(BYTES_ARRAY_VALUE) + .bind("p" + ++param) + .toDateArray(DATE_ARRAY_VALUE) + .bind("p" + ++param) + .toTimestampArray(TIMESTAMP_ARRAY_VALUE) + .build(); + } + + @After + public void clearRequests() { + mockSpanner.clearRequests(); + } + + @Test + public void testSelectAllTypes() { + try (Connection connection = createConnection()) { + try (ResultSet resultSet = connection.executeQuery(SELECT_STATEMENT)) { + assertTrue(resultSet.next()); + + int col = -1; + assertEquals(BOOL_VALUE, resultSet.getBoolean(++col)); + assertEquals(INT64_VALUE, resultSet.getLong(++col)); + assertEquals(FLOAT32_VALUE, resultSet.getFloat(++col), 0.0f); + assertEquals(FLOAT64_VALUE, resultSet.getDouble(++col), 0.0d); + if (dialect == Dialect.POSTGRESQL) { + assertEquals(PG_NUMERIC_VALUE, resultSet.getString(++col)); + } else { + assertEquals(NUMERIC_VALUE, resultSet.getBigDecimal(++col)); + } + assertEquals(STRING_VALUE, resultSet.getString(++col)); + assertEquals( + JSON_VALUE, + dialect == Dialect.POSTGRESQL ? resultSet.getPgJsonb(++col) : resultSet.getJson(++col)); + assertArrayEquals(BYTES_VALUE, resultSet.getBytes(++col).toByteArray()); + assertEquals(DATE_VALUE, resultSet.getDate(++col)); + assertEquals(TIMESTAMP_VALUE, resultSet.getTimestamp(++col)); + + assertEquals(BOOL_ARRAY_VALUE, resultSet.getBooleanList(++col)); + assertEquals(INT64_ARRAY_VALUE, resultSet.getLongList(++col)); + assertEquals(FLOAT32_ARRAY_VALUE, resultSet.getFloatList(++col)); + assertEquals(FLOAT64_ARRAY_VALUE, resultSet.getDoubleList(++col)); + if (dialect == Dialect.POSTGRESQL) { + assertEquals(PG_NUMERIC_ARRAY_VALUE, resultSet.getStringList(++col)); + } else { + assertEquals(NUMERIC_ARRAY_VALUE, resultSet.getBigDecimalList(++col)); + } + assertEquals(STRING_ARRAY_VALUE, resultSet.getStringList(++col)); + assertEquals( + JSON_ARRAY_VALUE, + dialect == Dialect.POSTGRESQL + ? resultSet.getPgJsonbList(++col) + : resultSet.getJsonList(++col)); + assertEquals(BYTES_ARRAY_VALUE, resultSet.getBytesList(++col)); + assertEquals(DATE_ARRAY_VALUE, resultSet.getDateList(++col)); + assertEquals(TIMESTAMP_ARRAY_VALUE, resultSet.getTimestampList(++col)); + + assertFalse(resultSet.next()); + } + } + } + + @Test + public void testInsertAllTypes() { + try (Connection connection = createConnection()) { + assertEquals(1L, connection.executeUpdate(createInsertStatement(dialect))); + + assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class)); + ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0); + Map paramTypes = request.getParamTypesMap(); + Map params = request.getParams().getFieldsMap(); + assertEquals(20, paramTypes.size()); + assertEquals(20, params.size()); + + // Verify param types. + ImmutableList expectedTypes = + ImmutableList.of( + TypeCode.BOOL, + TypeCode.INT64, + TypeCode.FLOAT32, + TypeCode.FLOAT64, + TypeCode.NUMERIC, + TypeCode.STRING, + TypeCode.JSON, + TypeCode.BYTES, + TypeCode.DATE, + TypeCode.TIMESTAMP); + for (int col = 0; col < expectedTypes.size(); col++) { + assertEquals(expectedTypes.get(col), paramTypes.get("p" + (col + 1)).getCode()); + int arrayCol = col + expectedTypes.size(); + assertEquals(TypeCode.ARRAY, paramTypes.get("p" + (arrayCol + 1)).getCode()); + assertEquals( + expectedTypes.get(col), + paramTypes.get("p" + (arrayCol + 1)).getArrayElementType().getCode()); + } + + // Verify param values. + int col = 0; + assertEquals(BOOL_VALUE, params.get("p" + ++col).getBoolValue()); + assertEquals(String.valueOf(INT64_VALUE), params.get("p" + ++col).getStringValue()); + assertEquals(FLOAT32_VALUE, params.get("p" + ++col).getNumberValue(), 0.0d); + assertEquals(FLOAT64_VALUE, params.get("p" + ++col).getNumberValue(), 0.0d); + assertEquals( + dialect == Dialect.POSTGRESQL ? PG_NUMERIC_VALUE : NUMERIC_VALUE.toEngineeringString(), + params.get("p" + ++col).getStringValue()); + assertEquals(STRING_VALUE, params.get("p" + ++col).getStringValue()); + assertEquals(JSON_VALUE, params.get("p" + ++col).getStringValue()); + assertEquals( + Base64.getEncoder().encodeToString(BYTES_VALUE), + params.get("p" + ++col).getStringValue()); + assertEquals(DATE_VALUE.toString(), params.get("p" + ++col).getStringValue()); + assertEquals(TIMESTAMP_VALUE.toString(), params.get("p" + ++col).getStringValue()); + + assertEquals( + BOOL_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map(value -> value.hasNullValue() ? null : value.getBoolValue()) + .collect(Collectors.toList())); + assertEquals( + INT64_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map(value -> value.hasNullValue() ? null : Long.valueOf(value.getStringValue())) + .collect(Collectors.toList())); + assertEquals( + FLOAT32_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map(value -> value.hasNullValue() ? null : (float) value.getNumberValue()) + .collect(Collectors.toList())); + assertEquals( + FLOAT64_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map(value -> value.hasNullValue() ? null : value.getNumberValue()) + .collect(Collectors.toList())); + if (dialect == Dialect.POSTGRESQL) { + assertEquals( + PG_NUMERIC_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map(value -> value.hasNullValue() ? null : value.getStringValue()) + .collect(Collectors.toList())); + } else { + assertEquals( + NUMERIC_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map(value -> value.hasNullValue() ? null : new BigDecimal(value.getStringValue())) + .collect(Collectors.toList())); + } + assertEquals( + STRING_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map(value -> value.hasNullValue() ? null : value.getStringValue()) + .collect(Collectors.toList())); + assertEquals( + JSON_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map(value -> value.hasNullValue() ? null : value.getStringValue()) + .collect(Collectors.toList())); + assertEquals( + BYTES_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map( + value -> + value.hasNullValue() ? null : ByteArray.fromBase64(value.getStringValue())) + .collect(Collectors.toList())); + assertEquals( + DATE_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map(value -> value.hasNullValue() ? null : Date.parseDate(value.getStringValue())) + .collect(Collectors.toList())); + assertEquals( + TIMESTAMP_ARRAY_VALUE, + params.get("p" + ++col).getListValue().getValuesList().stream() + .map( + value -> + value.hasNullValue() + ? null + : Timestamp.parseTimestamp(value.getStringValue())) + .collect(Collectors.toList())); + } + } +} From f8f835a9da705605c492e232a58276c39d1d7e6c Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 4 Mar 2024 15:04:48 +0100 Subject: [PATCH 13/16] deps: update dependency com.google.cloud:sdk-platform-java-config to v3.27.0 (#2935) --- .kokoro/presubmit/graalvm-native-17.cfg | 2 +- .kokoro/presubmit/graalvm-native.cfg | 2 +- google-cloud-spanner-bom/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-17.cfg index 5e86d37f07..56db68092d 100644 --- a/.kokoro/presubmit/graalvm-native-17.cfg +++ b/.kokoro/presubmit/graalvm-native-17.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.25.0" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.27.0" } env_vars: { diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native.cfg index 8e8cded782..6283184778 100644 --- a/.kokoro/presubmit/graalvm-native.cfg +++ b/.kokoro/presubmit/graalvm-native.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.25.0" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.27.0" } env_vars: { diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index db147dd9dc..3597efff8c 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -8,7 +8,7 @@ com.google.cloud sdk-platform-java-config - 3.25.0 + 3.27.0 Google Cloud Spanner BOM diff --git a/pom.xml b/pom.xml index 1bc7455aa3..c52b39da8c 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ com.google.cloud sdk-platform-java-config - 3.25.0 + 3.27.0 From 1d7044e97d16f5296b7de020cd24b11cbe2a7df0 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 4 Mar 2024 15:05:41 +0100 Subject: [PATCH 14/16] deps: update dependency org.json:json to v20240303 (#2936) --- google-cloud-spanner/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index 3c17a2a0de..e8e67e0659 100644 --- a/google-cloud-spanner/pom.xml +++ b/google-cloud-spanner/pom.xml @@ -388,7 +388,7 @@ org.json json - 20240205 + 20240303 test From 71ab685e932429db73294b5d1759d4895d93bf38 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 4 Mar 2024 15:06:13 +0100 Subject: [PATCH 15/16] test(deps): update dependency com.google.truth:truth to v1.4.2 (#2934) --- pom.xml | 2 +- samples/install-without-bom/pom.xml | 2 +- samples/native-image/pom.xml | 2 +- samples/snapshot/pom.xml | 2 +- samples/snippets/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index c52b39da8c..fea55f9e14 100644 --- a/pom.xml +++ b/pom.xml @@ -121,7 +121,7 @@ com.google.truth truth - 1.4.1 + 1.4.2 test diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 1d990d82e2..fb290694d6 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -100,7 +100,7 @@ com.google.truth truth - 1.4.1 + 1.4.2 test diff --git a/samples/native-image/pom.xml b/samples/native-image/pom.xml index 0c72603f1a..ea90374c40 100644 --- a/samples/native-image/pom.xml +++ b/samples/native-image/pom.xml @@ -51,7 +51,7 @@ com.google.truth truth - 1.4.1 + 1.4.2 test diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index d14be6eabd..86bd560834 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -99,7 +99,7 @@ com.google.truth truth - 1.4.1 + 1.4.2 test diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 4103b4ad8e..0d149d8e78 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -111,7 +111,7 @@ com.google.truth truth - 1.4.1 + 1.4.2 test From 95bad553c129e747be1fb958fdfe7224a2ae8192 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 15:20:16 +0000 Subject: [PATCH 16/16] chore(main): release 6.61.0 (#2926) :robot: I have created a release *beep* *boop* --- ## [6.61.0](https://togithub.com/googleapis/java-spanner/compare/v6.60.1...v6.61.0) (2024-03-04) ### Features * Support float32 type ([#2894](https://togithub.com/googleapis/java-spanner/issues/2894)) ([19b7976](https://togithub.com/googleapis/java-spanner/commit/19b79764294e938ad85d02b7c0662db6ec3afeda)) ### Bug Fixes * Flaky test issue due to AbortedException. ([#2925](https://togithub.com/googleapis/java-spanner/issues/2925)) ([cd34c1d](https://togithub.com/googleapis/java-spanner/commit/cd34c1d3ae9a5a36f4d5516dcf7c3667a9cf015a)) ### Dependencies * Update dependency com.google.cloud:sdk-platform-java-config to v3.27.0 ([#2935](https://togithub.com/googleapis/java-spanner/issues/2935)) ([f8f835a](https://togithub.com/googleapis/java-spanner/commit/f8f835a9da705605c492e232a58276c39d1d7e6c)) * Update dependency org.json:json to v20240303 ([#2936](https://togithub.com/googleapis/java-spanner/issues/2936)) ([1d7044e](https://togithub.com/googleapis/java-spanner/commit/1d7044e97d16f5296b7de020cd24b11cbe2a7df0)) ### Documentation * Samples and tests for backup Admin APIs and overall spanner Admin APIs. ([#2882](https://togithub.com/googleapis/java-spanner/issues/2882)) ([de13636](https://togithub.com/googleapis/java-spanner/commit/de1363645e03f46deed5be41f90ddfed72766751)) * Update all public documents to use auto-generated admin clients. ([#2928](https://togithub.com/googleapis/java-spanner/issues/2928)) ([ccb110a](https://togithub.com/googleapis/java-spanner/commit/ccb110ad6835557870933c95cfd76580fd317a16)) --- This PR was generated with [Release Please](https://togithub.com/googleapis/release-please). See [documentation](https://togithub.com/googleapis/release-please#release-please). --- CHANGELOG.md | 24 +++++++++++++++++++ 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 ++++++++-------- 15 files changed, 74 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7eae29cd8..c2b269ee2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## [6.61.0](https://github.com/googleapis/java-spanner/compare/v6.60.1...v6.61.0) (2024-03-04) + + +### Features + +* Support float32 type ([#2894](https://github.com/googleapis/java-spanner/issues/2894)) ([19b7976](https://github.com/googleapis/java-spanner/commit/19b79764294e938ad85d02b7c0662db6ec3afeda)) + + +### Bug Fixes + +* Flaky test issue due to AbortedException. ([#2925](https://github.com/googleapis/java-spanner/issues/2925)) ([cd34c1d](https://github.com/googleapis/java-spanner/commit/cd34c1d3ae9a5a36f4d5516dcf7c3667a9cf015a)) + + +### Dependencies + +* Update dependency com.google.cloud:sdk-platform-java-config to v3.27.0 ([#2935](https://github.com/googleapis/java-spanner/issues/2935)) ([f8f835a](https://github.com/googleapis/java-spanner/commit/f8f835a9da705605c492e232a58276c39d1d7e6c)) +* Update dependency org.json:json to v20240303 ([#2936](https://github.com/googleapis/java-spanner/issues/2936)) ([1d7044e](https://github.com/googleapis/java-spanner/commit/1d7044e97d16f5296b7de020cd24b11cbe2a7df0)) + + +### Documentation + +* Samples and tests for backup Admin APIs and overall spanner Admin APIs. ([#2882](https://github.com/googleapis/java-spanner/issues/2882)) ([de13636](https://github.com/googleapis/java-spanner/commit/de1363645e03f46deed5be41f90ddfed72766751)) +* Update all public documents to use auto-generated admin clients. ([#2928](https://github.com/googleapis/java-spanner/issues/2928)) ([ccb110a](https://github.com/googleapis/java-spanner/commit/ccb110ad6835557870933c95cfd76580fd317a16)) + ## [6.60.1](https://github.com/googleapis/java-spanner/compare/v6.60.0...v6.60.1) (2024-02-23) diff --git a/google-cloud-spanner-bom/pom.xml b/google-cloud-spanner-bom/pom.xml index 3597efff8c..8df354deb8 100644 --- a/google-cloud-spanner-bom/pom.xml +++ b/google-cloud-spanner-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner-bom - 6.60.2-SNAPSHOT + 6.61.0 pom com.google.cloud @@ -53,43 +53,43 @@ com.google.cloud google-cloud-spanner - 6.60.2-SNAPSHOT + 6.61.0 com.google.cloud google-cloud-spanner test-jar - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/google-cloud-spanner-executor/pom.xml b/google-cloud-spanner-executor/pom.xml index a8a8f96719..495d9edfb9 100644 --- a/google-cloud-spanner-executor/pom.xml +++ b/google-cloud-spanner-executor/pom.xml @@ -5,14 +5,14 @@ 4.0.0 com.google.cloud google-cloud-spanner-executor - 6.60.2-SNAPSHOT + 6.61.0 jar Google Cloud Spanner Executor com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/google-cloud-spanner/pom.xml b/google-cloud-spanner/pom.xml index e8e67e0659..b2e0351c0a 100644 --- a/google-cloud-spanner/pom.xml +++ b/google-cloud-spanner/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-spanner - 6.60.2-SNAPSHOT + 6.61.0 jar Google Cloud Spanner https://github.com/googleapis/java-spanner @@ -11,7 +11,7 @@ com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 google-cloud-spanner diff --git a/grpc-google-cloud-spanner-admin-database-v1/pom.xml b/grpc-google-cloud-spanner-admin-database-v1/pom.xml index b1677e6299..e55efda7d9 100644 --- a/grpc-google-cloud-spanner-admin-database-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.60.2-SNAPSHOT + 6.61.0 grpc-google-cloud-spanner-admin-database-v1 GRPC library for grpc-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml index 2e6b57966c..aead92c9a1 100644 --- a/grpc-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/grpc-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.60.2-SNAPSHOT + 6.61.0 grpc-google-cloud-spanner-admin-instance-v1 GRPC library for grpc-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/grpc-google-cloud-spanner-executor-v1/pom.xml b/grpc-google-cloud-spanner-executor-v1/pom.xml index 0572282d5d..cc007bd888 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.0 com.google.api.grpc grpc-google-cloud-spanner-executor-v1 - 6.60.2-SNAPSHOT + 6.61.0 grpc-google-cloud-spanner-executor-v1 GRPC library for google-cloud-spanner com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/grpc-google-cloud-spanner-v1/pom.xml b/grpc-google-cloud-spanner-v1/pom.xml index 7595a5a451..941536483c 100644 --- a/grpc-google-cloud-spanner-v1/pom.xml +++ b/grpc-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.60.2-SNAPSHOT + 6.61.0 grpc-google-cloud-spanner-v1 GRPC library for grpc-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/pom.xml b/pom.xml index fea55f9e14..bb0fb156d0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-spanner-parent pom - 6.60.2-SNAPSHOT + 6.61.0 Google Cloud Spanner Parent https://github.com/googleapis/java-spanner @@ -61,47 +61,47 @@ com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc proto-google-cloud-spanner-executor-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc grpc-google-cloud-spanner-executor-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc grpc-google-cloud-spanner-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc grpc-google-cloud-spanner-admin-instance-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.api.grpc grpc-google-cloud-spanner-admin-database-v1 - 6.60.2-SNAPSHOT + 6.61.0 com.google.cloud google-cloud-spanner - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/proto-google-cloud-spanner-admin-database-v1/pom.xml b/proto-google-cloud-spanner-admin-database-v1/pom.xml index 752aae6b95..5e6f00a6fc 100644 --- a/proto-google-cloud-spanner-admin-database-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-database-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-database-v1 - 6.60.2-SNAPSHOT + 6.61.0 proto-google-cloud-spanner-admin-database-v1 PROTO library for proto-google-cloud-spanner-admin-database-v1 com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/proto-google-cloud-spanner-admin-instance-v1/pom.xml b/proto-google-cloud-spanner-admin-instance-v1/pom.xml index 626c0c8f13..205dfdcc1b 100644 --- a/proto-google-cloud-spanner-admin-instance-v1/pom.xml +++ b/proto-google-cloud-spanner-admin-instance-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-admin-instance-v1 - 6.60.2-SNAPSHOT + 6.61.0 proto-google-cloud-spanner-admin-instance-v1 PROTO library for proto-google-cloud-spanner-admin-instance-v1 com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/proto-google-cloud-spanner-executor-v1/pom.xml b/proto-google-cloud-spanner-executor-v1/pom.xml index eb6669fb12..e8dcec3683 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.0 com.google.api.grpc proto-google-cloud-spanner-executor-v1 - 6.60.2-SNAPSHOT + 6.61.0 proto-google-cloud-spanner-executor-v1 Proto library for google-cloud-spanner com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/proto-google-cloud-spanner-v1/pom.xml b/proto-google-cloud-spanner-v1/pom.xml index e87c94e5aa..7c9f074e92 100644 --- a/proto-google-cloud-spanner-v1/pom.xml +++ b/proto-google-cloud-spanner-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-spanner-v1 - 6.60.2-SNAPSHOT + 6.61.0 proto-google-cloud-spanner-v1 PROTO library for proto-google-cloud-spanner-v1 com.google.cloud google-cloud-spanner-parent - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 86bd560834..298e4eab43 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -32,7 +32,7 @@ com.google.cloud google-cloud-spanner - 6.60.2-SNAPSHOT + 6.61.0 diff --git a/versions.txt b/versions.txt index 8d82efe3b9..42f18614e5 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.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 +proto-google-cloud-spanner-admin-instance-v1:6.61.0:6.61.0 +proto-google-cloud-spanner-v1:6.61.0:6.61.0 +proto-google-cloud-spanner-admin-database-v1:6.61.0:6.61.0 +grpc-google-cloud-spanner-v1:6.61.0:6.61.0 +grpc-google-cloud-spanner-admin-instance-v1:6.61.0:6.61.0 +grpc-google-cloud-spanner-admin-database-v1:6.61.0:6.61.0 +google-cloud-spanner:6.61.0:6.61.0 +google-cloud-spanner-executor:6.61.0:6.61.0 +proto-google-cloud-spanner-executor-v1:6.61.0:6.61.0 +grpc-google-cloud-spanner-executor-v1:6.61.0:6.61.0