From edc5bbf0d9d4faf48fd9a8d479d5bc5de938c82d Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 6 Feb 2023 13:29:38 +0530 Subject: [PATCH 01/30] 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 --- .../com/google/cloud/spanner/it/ITClosedSessionTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java index aeb0256285b..227611a10de 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java @@ -251,7 +251,10 @@ public void testTransactionManager() throws InterruptedException { break; } } catch (AbortedException e) { - Thread.sleep(e.getRetryDelayInMillis()); + long retryDelayInMillis = e.getRetryDelayInMillis(); + if(retryDelayInMillis > 0) { + Thread.sleep(retryDelayInMillis); + } txn = manager.resetForRetry(); } } From 4cd497b05eab3e3b6b89b582bfafde80d42c1518 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Wed, 8 Feb 2023 15:27:18 +0530 Subject: [PATCH 02/30] Fixing lint issues. --- .../java/com/google/cloud/spanner/it/ITClosedSessionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java index 227611a10de..efbffcfa899 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java @@ -252,7 +252,7 @@ public void testTransactionManager() throws InterruptedException { } } catch (AbortedException e) { long retryDelayInMillis = e.getRetryDelayInMillis(); - if(retryDelayInMillis > 0) { + if (retryDelayInMillis > 0) { Thread.sleep(retryDelayInMillis); } txn = manager.resetForRetry(); From 37759969d694c86f8d67a25ebaa9b0ebc4f1f888 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Wed, 10 Jan 2024 09:44:13 +0530 Subject: [PATCH 03/30] chore: copy samples and rewrite create backup. --- .../admin/generated/CopyBackupSample.java | 101 + .../CreateBackupWithEncryptionKey.java | 107 + .../CreateDatabaseWithEncryptionKey.java | 100 + .../admin/generated/PgSpannerSample.java | 1584 ++++++++++++ .../RestoreBackupWithEncryptionKey.java | 92 + .../admin/generated/SpannerSample.java | 2224 +++++++++++++++++ 6 files changed, 4208 insertions(+) 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 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 00000000000..2d2275b5999 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/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.generated; + +// [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/generated/CreateBackupWithEncryptionKey.java new file mode 100644 index 00000000000..890e0a614ad --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java @@ -0,0 +1,107 @@ +/* + * 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.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 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 InterruptedException { + // 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 (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + createBackupWithEncryptionKey( + adminClient, + projectId, + instanceId, + databaseId, + backupId, + kmsKeyName); + } + } + + static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient, + String projectId, String instanceId, String databaseId, String backupId, String kmsKeyName) + throws InterruptedException { + // 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); + + Backup backup; + try { + System.out.println("Waiting for operation to complete..."); + backup = operation.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.getId().getName(), + backup.getSize(), + LocalDateTime.ofEpochSecond( + backup.getProto().getCreateTime().getSeconds(), + backup.getProto().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 00000000000..cd50f5ed6fd --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateDatabaseWithEncryptionKey.java @@ -0,0 +1,100 @@ +/* + * 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.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 java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class CreateDatabaseWithEncryptionKey { + + static void createDatabaseWithEncryptionKey() { + // 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 (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + createDatabaseWithEncryptionKey( + adminClient, + projectId, + instanceId, + databaseId, + kmsKeyName); + } + } + + 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)) + .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); + + System.out.printf( + "Database %s created with encryption key %s%n", + createdDatabase.getId(), + 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 00000000000..2c0c6a71125 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/PgSpannerSample.java @@ -0,0 +1,1584 @@ +/* + * 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.longrunning.OperationFuture; +import com.google.api.gax.paging.Page; +import com.google.cloud.ByteArray; +import com.google.cloud.Date; +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceId; +import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.KeyRange; +import com.google.cloud.spanner.KeySet; +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; +import com.google.cloud.spanner.ReadOnlyTransaction; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerBatchUpdateException; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.Struct; +import com.google.cloud.spanner.TimestampBound; +import com.google.cloud.spanner.Value; +import com.google.common.io.BaseEncoding; +import com.google.longrunning.Operation; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.spanner.admin.database.v1.CreateBackupMetadata; +import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import com.google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import com.google.spanner.v1.ExecuteSqlRequest; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * Example code for using the Cloud Spanner PostgreSQL interface. + */ +public class PgSpannerSample { + // [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, DatabaseId id) { + OperationFuture op = dbAdminClient.createDatabase( + dbAdminClient.newDatabaseBuilder(id).setDialect(Dialect.POSTGRESQL).build(), + Collections.emptyList()); + try { + // Initiate the request which returns an OperationFuture. + Database db = op.get(); + System.out.println("Created database [" + db.getId() + "]"); + createTableUsingDdl(dbAdminClient, id); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_database] + + // [START spanner_postgresql_insert_data] + static void writeExampleData(DatabaseClient dbClient) { + List mutations = new ArrayList<>(); + for (Singer singer : SINGERS) { + mutations.add( + Mutation.newInsertBuilder("Singers") + .set("SingerId") + .to(singer.singerId) + .set("FirstName") + .to(singer.firstName) + .set("LastName") + .to(singer.lastName) + .build()); + } + for (Album album : ALBUMS) { + mutations.add( + Mutation.newInsertBuilder("Albums") + .set("SingerId") + .to(album.singerId) + .set("AlbumId") + .to(album.albumId) + .set("AlbumTitle") + .to(album.albumTitle) + .build()); + } + dbClient.write(mutations); + } + // [END spanner_postgresql_insert_data] + + // [START spanner_postgresql_delete_data] + static void deleteExampleData(DatabaseClient dbClient) { + List mutations = new ArrayList<>(); + + // KeySet.Builder can be used to delete a specific set of rows. + // Delete the Albums with the key values (2,1) and (2,3). + mutations.add( + Mutation.delete( + "Albums", KeySet.newBuilder().addKey(Key.of(2, 1)).addKey(Key.of(2, 3)).build())); + + // KeyRange can be used to delete rows with a key in a specific range. + // Delete a range of rows where the column key is >=3 and <5 + mutations.add( + Mutation.delete("Singers", KeySet.range(KeyRange.closedOpen(Key.of(3), Key.of(5))))); + + // KeySet.all() can be used to delete all the rows in a table. + // Delete remaining Singers rows, which will also delete the remaining Albums rows since it was + // defined with ON DELETE CASCADE. + mutations.add(Mutation.delete("Singers", KeySet.all())); + + dbClient.write(mutations); + System.out.printf("Records deleted.\n"); + } + // [END spanner_postgresql_delete_data] + + // [START spanner_postgresql_query_data] + static void query(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() // Execute a single read or query against Cloud Spanner. + .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), + resultSet.getString(2)); + } + } + } + // [END spanner_postgresql_query_data] + + // [START spanner_postgresql_read_data] + static void read(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() + .read( + "Albums", + KeySet.all(), // Read all rows in a table. + Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), + resultSet.getString(2)); + } + } + } + // [END spanner_postgresql_read_data] + + // [START spanner_postgresql_add_column] + static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget bigint"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Added MarketingBudget column"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_add_column] + + // Before executing this method, a new column MarketingBudget has to be added to the Albums + // table by applying the DDL statement "ALTER TABLE Albums ADD COLUMN MarketingBudget INT64". + // [START spanner_postgresql_update_data] + static void update(DatabaseClient dbClient) { + // Mutation can be used to update/insert/delete a single row in a table. Here we use + // newUpdateBuilder to create update mutations. + List mutations = + Arrays.asList( + Mutation.newUpdateBuilder("Albums") + .set("SingerId") + .to(1) + .set("AlbumId") + .to(1) + .set("MarketingBudget") + .to(100000) + .build(), + Mutation.newUpdateBuilder("Albums") + .set("SingerId") + .to(2) + .set("AlbumId") + .to(2) + .set("MarketingBudget") + .to(500000) + .build()); + // This writes all the mutations to Cloud Spanner atomically. + dbClient.write(mutations); + } + // [END spanner_postgresql_update_data] + + // [START spanner_postgresql_read_write_transaction] + static void writeWithTransaction(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + // Transfer marketing budget from one album to another. We do it in a transaction to + // ensure that the transfer is atomic. + Struct row = + transaction.readRow("Albums", Key.of(2, 2), Arrays.asList("MarketingBudget")); + long album2Budget = row.getLong(0); + // Transaction will only be committed if this condition still holds at the time of + // commit. Otherwise it will be aborted and the callable will be rerun by the + // client library. + long transfer = 200000; + if (album2Budget >= transfer) { + long album1Budget = + transaction + .readRow("Albums", Key.of(1, 1), Arrays.asList("MarketingBudget")) + .getLong(0); + album1Budget += transfer; + album2Budget -= transfer; + transaction.buffer( + Mutation.newUpdateBuilder("Albums") + .set("SingerId") + .to(1) + .set("AlbumId") + .to(1) + .set("MarketingBudget") + .to(album1Budget) + .build()); + transaction.buffer( + Mutation.newUpdateBuilder("Albums") + .set("SingerId") + .to(2) + .set("AlbumId") + .to(2) + .set("MarketingBudget") + .to(album2Budget) + .build()); + } + return null; + }); + } + // [END spanner_postgresql_read_write_transaction] + + // [START spanner_postgresql_query_data_with_new_column] + static void queryMarketingBudget(DatabaseClient dbClient) { + // Rows without an explicit value for MarketingBudget will have a MarketingBudget equal to + // null. A try-with-resource block is used to automatically release resources held by + // ResultSet. + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery(Statement.of("SELECT singerid as \"SingerId\", " + + "albumid as \"AlbumId\", marketingbudget as \"MarketingBudget\" " + + "FROM Albums"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", + resultSet.getLong("SingerId"), + resultSet.getLong("AlbumId"), + // We check that the value is non null. ResultSet getters can only be used to retrieve + // non null values. + resultSet.isNull("MarketingBudget") ? "NULL" : + resultSet.getLong("MarketingBudget")); + } + } + } + // [END spanner_postgresql_query_data_with_new_column] + + // [START spanner_postgresql_create_index] + static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Added AlbumsByAlbumTitle index"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_index] + + // [START spanner_postgresql_read_data_with_index] + static void readUsingIndex(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() + .readUsingIndex( + "Albums", + "AlbumsByAlbumTitle", + KeySet.all(), + Arrays.asList("AlbumId", "AlbumTitle"))) { + while (resultSet.next()) { + System.out.printf("%d %s\n", resultSet.getLong(0), resultSet.getString(1)); + } + } + } + // [END spanner_postgresql_read_data_with_index] + + // [START spanner_postgresql_create_storing_index] + static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList( + "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) " + + "INCLUDE (MarketingBudget)"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Added AlbumsByAlbumTitle2 index"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_storing_index] + + // Before running this example, create a storing index AlbumsByAlbumTitle2 by applying the DDL + // statement "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) INCLUDE (MarketingBudget)". + // [START spanner_postgresql_read_data_with_storing_index] + static void readStoringIndex(DatabaseClient dbClient) { + // We can read MarketingBudget also from the index since it stores a copy of MarketingBudget. + try (ResultSet resultSet = + dbClient + .singleUse() + .readUsingIndex( + "Albums", + "AlbumsByAlbumTitle2", + KeySet.all(), + Arrays.asList("AlbumId", "AlbumTitle", "MarketingBudget"))) { + while (resultSet.next()) { + System.out.printf( + "%d %s %s\n", + resultSet.getLong(0), + resultSet.getString(1), + resultSet.isNull("marketingbudget") ? "NULL" : resultSet.getLong(2)); + } + } + } + // [END spanner_postgresql_read_data_with_storing_index] + + // [START spanner_postgresql_read_only_transaction] + static void readOnlyTransaction(DatabaseClient dbClient) { + // ReadOnlyTransaction must be closed by calling close() on it to release resources held by it. + // We use a try-with-resource block to automatically do so. + try (ReadOnlyTransaction transaction = dbClient.readOnlyTransaction()) { + ResultSet queryResultSet = + transaction.executeQuery( + Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums")); + while (queryResultSet.next()) { + System.out.printf( + "%d %d %s\n", + queryResultSet.getLong(0), queryResultSet.getLong(1), + queryResultSet.getString(2)); + } + try (ResultSet readResultSet = + transaction.read( + "Albums", KeySet.all(), Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) { + while (readResultSet.next()) { + System.out.printf( + "%d %d %s\n", + readResultSet.getLong(0), readResultSet.getLong(1), + readResultSet.getString(2)); + } + } + } + } + // [END spanner_postgresql_read_only_transaction] + + // [START spanner_postgresql_query_singers_table] + static void querySingersTable(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery(Statement.of("SELECT singerid as \"SingerId\", " + + "firstname as \"FirstName\", lastname as \"LastName\" FROM Singers"))) { + while (resultSet.next()) { + System.out.printf( + "%s %s %s\n", + resultSet.getLong("SingerId"), + resultSet.getString("FirstName"), + resultSet.getString("LastName")); + } + } + } + // [END spanner_postgresql_query_singers_table] + + + // [START spanner_postgresql_dml_getting_started_insert] + static void writeUsingDml(DatabaseClient dbClient) { + // Insert 4 singer records + dbClient + .readWriteTransaction() + .run(transaction -> { + String sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES " + + "(12, 'Melissa', 'Garcia'), " + + "(13, 'Russell', 'Morales'), " + + "(14, 'Jacqueline', 'Long'), " + + "(15, 'Dylan', 'Shaw')"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d records inserted.\n", rowCount); + return null; + }); + } + // [END spanner_postgresql_dml_getting_started_insert] + + // [START spanner_postgresql_query_with_parameter] + static void queryWithParameter(DatabaseClient dbClient) { + Statement statement = + Statement.newBuilder( + "SELECT singerid AS \"SingerId\", " + + "firstname as \"FirstName\", lastname as \"LastName\" " + + "FROM Singers " + + "WHERE LastName = $1") + .bind("p1") + .to("Garcia") + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %s\n", + resultSet.getLong("SingerId"), + resultSet.getString("FirstName"), + resultSet.getString("LastName")); + } + } + } + // [END spanner_postgresql_query_with_parameter] + + // [START spanner_postgresql_dml_getting_started_update] + static void writeWithTransactionUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + // Transfer marketing budget from one album to another. We do it in a transaction to + // ensure that the transfer is atomic. + String sql1 = + "SELECT marketingbudget as \"MarketingBudget\" from Albums WHERE " + + "SingerId = 2 and AlbumId = 2"; + ResultSet resultSet = transaction.executeQuery(Statement.of(sql1)); + long album2Budget = 0; + while (resultSet.next()) { + album2Budget = resultSet.getLong("MarketingBudget"); + } + // Transaction will only be committed if this condition still holds at the time of + // commit. Otherwise it will be aborted and the callable will be rerun by the + // client library. + long transfer = 200000; + if (album2Budget >= transfer) { + String sql2 = + "SELECT marketingbudget as \"MarketingBudget\" from Albums WHERE " + + "SingerId = 1 and AlbumId = 1"; + ResultSet resultSet2 = transaction.executeQuery(Statement.of(sql2)); + long album1Budget = 0; + while (resultSet2.next()) { + album1Budget = resultSet2.getLong("MarketingBudget"); + } + album1Budget += transfer; + album2Budget -= transfer; + Statement updateStatement = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1" + + "WHERE SingerId = 1 and AlbumId = 1") + .bind("p1") + .to(album1Budget) + .build(); + transaction.executeUpdate(updateStatement); + Statement updateStatement2 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1" + + "WHERE SingerId = 2 and AlbumId = 2") + .bind("p1") + .to(album2Budget) + .build(); + transaction.executeUpdate(updateStatement2); + } + return null; + }); + } + // [END spanner_postgresql_dml_getting_started_update] + + // [START spanner_postgresql_create_table_using_ddl] + // [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); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Created Singers & Albums tables in database: [" + id + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_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, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList( + "ALTER TABLE Albums ADD COLUMN LastUpdateTime spanner.commit_timestamp"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Added LastUpdateTime as a timestamp column in Albums table."); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_add_timestamp_column] + + // [START spanner_postgresql_query_data_with_timestamp_column] + static void queryMarketingBudgetWithTimestamp(DatabaseClient dbClient) { + // Rows without an explicit value for MarketingBudget will have a MarketingBudget equal to + // null. A try-with-resource block is used to automatically release resources held by + // ResultSet. + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery( + Statement.of( + "SELECT singerid as \"SingerId\", albumid as \"AlbumId\", " + + "marketingbudget as \"MarketingBudget\"," + + "lastupdatetime as \"LastUpdateTime\" FROM Albums" + + " ORDER BY LastUpdateTime DESC"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s %s\n", + resultSet.getLong("SingerId"), + resultSet.getLong("AlbumId"), + // We check that the value is non null. ResultSet getters can only be used to retrieve + // non null values. + resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"), + resultSet.isNull("LastUpdateTime") ? "NULL" : resultSet.getTimestamp("LastUpdateTime")); + } + } + } + // [END spanner_postgresql_query_data_with_timestamp_column] + + // [START spanner_postgresql_create_table_with_timestamp_column] + static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Performances (" + + " SingerId BIGINT NOT NULL," + + " VenueId BIGINT NOT NULL," + + " Revenue BIGINT," + + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," + + " PRIMARY KEY (SingerId, VenueId))" + + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Created Performances table in database: [" + id + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_table_with_timestamp_column] + + // [START spanner_postgresql_insert_data_with_timestamp_column] + static void writeExampleDataWithTimestamp(DatabaseClient dbClient) { + List mutations = new ArrayList<>(); + for (Performance performance : PERFORMANCES) { + mutations.add( + Mutation.newInsertBuilder("Performances") + .set("SingerId") + .to(performance.singerId) + .set("VenueId") + .to(performance.venueId) + .set("Revenue") + .to(performance.revenue) + .set("LastUpdateTime") + .to(Value.COMMIT_TIMESTAMP) + .build()); + } + dbClient.write(mutations); + } + // [END spanner_postgresql_insert_data_with_timestamp_column] + + static void queryPerformancesTable(DatabaseClient dbClient) { + // Rows without an explicit value for Revenue will have a Revenue equal to + // null. A try-with-resource block is used to automatically release resources held by + // ResultSet. + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery( + Statement.of( + "SELECT singerid as \"SingerId\", venueid as \"VenueId\", " + + "revenue as \"Revenue\", lastupdatetime as \"LastUpdateTime\" " + + "FROM Performances ORDER BY LastUpdateTime DESC"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s %s\n", + resultSet.getLong("SingerId"), + resultSet.getLong("VenueId"), + // We check that the value is non null. ResultSet getters can only be used to retrieve + // non null values. + resultSet.isNull("Revenue") ? "NULL" : resultSet.getLong("Revenue"), + resultSet.getTimestamp("LastUpdateTime")); + } + } + } + + // [START spanner_postgresql_dml_standard_insert] + static void insertUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + String sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) " + + " VALUES (10, 'Virginia', 'Watson')"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record inserted.\n", rowCount); + return null; + }); + } + // [END spanner_postgresql_dml_standard_insert] + + // [START spanner_postgresql_dml_standard_update] + static void updateUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + String sql = + "UPDATE Albums " + + "SET MarketingBudget = MarketingBudget * 2 " + + "WHERE SingerId = 1 and AlbumId = 1"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record updated.\n", rowCount); + return null; + }); + } + // [END spanner_postgresql_dml_standard_update] + + // [START spanner_postgresql_dml_standard_delete] + static void deleteUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + String sql = "DELETE FROM Singers WHERE FirstName = 'Alice'"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record deleted.\n", rowCount); + return null; + }); + } + // [END spanner_postgresql_dml_standard_delete] + + // [START spanner_postgresql_dml_write_then_read] + static void writeAndReadUsingDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + // Insert record. + String sql = + "INSERT INTO Singers (SingerId, FirstName, LastName) " + + " VALUES (11, 'Timothy', 'Campbell')"; + long rowCount = transaction.executeUpdate(Statement.of(sql)); + System.out.printf("%d record inserted.\n", rowCount); + // Read newly inserted record. + sql = "SELECT firstname as \"FirstName\", lastname as \"LastName\" FROM Singers WHERE " + + "SingerId = 11"; + // We use a try-with-resource block to automatically release resources held by + // ResultSet. + try (ResultSet resultSet = transaction.executeQuery(Statement.of(sql))) { + while (resultSet.next()) { + System.out.printf( + "%s %s\n", + resultSet.getString("FirstName"), resultSet.getString("LastName")); + } + } + return null; + }); + } + // [END spanner_postgresql_dml_write_then_read] + + // [START spanner_postgresql_dml_partitioned_update] + static void updateUsingPartitionedDml(DatabaseClient dbClient) { + String sql = "UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1"; + long rowCount = dbClient.executePartitionedUpdate(Statement.of(sql)); + System.out.printf("%d records updated.\n", rowCount); + } + // [END spanner_postgresql_dml_partitioned_update] + + // [START spanner_postgresql_dml_partitioned_delete] + static void deleteUsingPartitionedDml(DatabaseClient dbClient) { + String sql = "DELETE FROM Singers WHERE SingerId > 10"; + long rowCount = dbClient.executePartitionedUpdate(Statement.of(sql)); + System.out.printf("%d records deleted.\n", rowCount); + } + // [END spanner_postgresql_dml_partitioned_delete] + + // [START spanner_postgresql_dml_batch_update] + static void updateUsingBatchDml(DatabaseClient dbClient) { + dbClient + .readWriteTransaction() + .run(transaction -> { + List stmts = new ArrayList(); + String sql = + "INSERT INTO Albums " + + "(SingerId, AlbumId, AlbumTitle, MarketingBudget) " + + "VALUES (1, 3, 'Test Album Title', 10000) "; + stmts.add(Statement.of(sql)); + sql = + "UPDATE Albums " + + "SET MarketingBudget = MarketingBudget * 2 " + + "WHERE SingerId = 1 and AlbumId = 3"; + stmts.add(Statement.of(sql)); + long[] rowCounts; + try { + rowCounts = transaction.batchUpdate(stmts); + } catch (SpannerBatchUpdateException e) { + rowCounts = e.getUpdateCounts(); + } + for (int i = 0; i < rowCounts.length; i++) { + System.out.printf("%d record updated by stmt %d.\n", rowCounts[i], i); + } + return null; + }); + } + // [END spanner_postgresql_dml_batch_update] + + // [START spanner_postgresql_create_table_with_datatypes] + static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Venues (" + + " VenueId BIGINT NOT NULL," + + " VenueName character varying(100)," + + " VenueInfo bytea," + + " Capacity BIGINT," + + " OutdoorVenue BOOL, " + + " PopularityScore FLOAT8, " + + " Revenue NUMERIC, " + + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," + + " PRIMARY KEY (VenueId))"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Created Venues table in database: [" + id + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_postgresql_create_table_with_datatypes] + + // [START spanner_postgresql_insert_datatypes_data] + static void writeDatatypesData(DatabaseClient dbClient) { + List mutations = new ArrayList<>(); + for (Venue venue : VENUES) { + mutations.add( + Mutation.newInsertBuilder("Venues") + .set("VenueId") + .to(venue.venueId) + .set("VenueName") + .to(venue.venueName) + .set("VenueInfo") + .to(venue.venueInfo) + .set("Capacity") + .to(venue.capacity) + .set("OutdoorVenue") + .to(venue.outdoorVenue) + .set("PopularityScore") + .to(venue.popularityScore) + .set("Revenue") + .to(venue.revenue) + .set("LastUpdateTime") + .to(Value.COMMIT_TIMESTAMP) + .build()); + } + dbClient.write(mutations); + } + // [END spanner_postgresql_insert_datatypes_data] + + // [START spanner_postgresql_query_with_bool_parameter] + static void queryWithBool(DatabaseClient dbClient) { + boolean exampleBool = true; + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\"," + + " outdoorvenue as \"OutdoorVenue\" FROM Venues " + + "WHERE OutdoorVenue = $1") + .bind("p1") + .to(exampleBool) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %b\n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getBoolean("OutdoorVenue")); + } + } + } + // [END spanner_postgresql_query_with_bool_parameter] + + // [START spanner_postgresql_query_with_bytes_parameter] + static void queryWithBytes(DatabaseClient dbClient) { + ByteArray exampleBytes = + ByteArray.fromBase64(BaseEncoding.base64().encode("Hello World 1".getBytes())); + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\" FROM Venues " + + "WHERE VenueInfo = $1") + .bind("p1") + .to(exampleBytes) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s\n", resultSet.getLong("VenueId"), resultSet.getString("VenueName")); + } + } + } + // [END spanner_postgresql_query_with_bytes_parameter] + + // [START spanner_postgresql_query_with_float_parameter] + static void queryWithFloat(DatabaseClient dbClient) { + float exampleFloat = 0.8f; + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\", " + + "popularityscore as \"PopularityScore\" FROM Venues " + + "WHERE PopularityScore > $1") + .bind("p1") + .to(exampleFloat) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %f\n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getDouble("PopularityScore")); + } + } + } + // [END spanner_postgresql_query_with_float_parameter] + + // [START spanner_postgresql_query_with_int_parameter] + static void queryWithInt(DatabaseClient dbClient) { + long exampleInt = 3000; + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\", " + + "capacity as \"Capacity\" " + + "FROM Venues " + "WHERE Capacity >= $1") + .bind("p1") + .to(exampleInt) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %d\n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getLong("Capacity")); + } + } + } + // [END spanner_postgresql_query_with_int_parameter] + + // [START spanner_postgresql_query_with_string_parameter] + static void queryWithString(DatabaseClient dbClient) { + String exampleString = "Venue 42"; + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\" FROM Venues WHERE" + + " VenueName = $1") + .bind("p1") + .to(exampleString) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s\n", resultSet.getLong("VenueId"), resultSet.getString("VenueName")); + } + } + } + // [END spanner_postgresql_query_with_string_parameter] + + // [START spanner_postgresql_query_with_timestamp_parameter] + static void queryWithTimestampParameter(DatabaseClient dbClient) { + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\", " + + "lastupdatetime as \"LastUpdateTime\" FROM Venues " + + "WHERE LastUpdateTime < $1") + .bind("p1") + .to(Timestamp.now()) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %s\n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getTimestamp("LastUpdateTime")); + } + } + } + // [END spanner_postgresql_query_with_timestamp_parameter] + + // [START spanner_postgresql_query_with_numeric_parameter] + static void queryWithNumeric(DatabaseClient dbClient) { + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", venuename as \"VenueName\", " + + "revenue as \"Revenue\" FROM Venues\n" + + "WHERE Revenue >= $1") + .bind("p1") + .to(Value.pgNumeric("300000")) + .build(); + try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s %s%n", + resultSet.getLong("VenueId"), + resultSet.getString("VenueName"), + resultSet.getValue("Revenue")); + } + } + } + // [END spanner_postgresql_query_with_numeric_parameter] + + // [START spanner_postgresql_create_client_with_query_options] + static void clientWithQueryOptions(DatabaseId db) { + SpannerOptions options = + SpannerOptions.newBuilder() + .setDefaultQueryOptions( + db, ExecuteSqlRequest.QueryOptions + .newBuilder() + .setOptimizerVersion("1") + // The list of available statistics packages can be found by querying the + // "INFORMATION_SCHEMA.spanner_postgresql_STATISTICS" table. + .setOptimizerStatisticsPackage("latest") + .build()) + .build(); + Spanner spanner = options.getService(); + DatabaseClient dbClient = spanner.getDatabaseClient(db); + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2)); + } + } + } + // [END spanner_postgresql_create_client_with_query_options] + + // [START spanner_postgresql_query_with_query_options] + static void queryWithQueryOptions(DatabaseClient dbClient) { + try (ResultSet resultSet = + dbClient + .singleUse() + .executeQuery( + Statement + .newBuilder("SELECT SingerId, AlbumId, AlbumTitle FROM Albums") + .withQueryOptions(ExecuteSqlRequest.QueryOptions + .newBuilder() + .setOptimizerVersion("1") + // The list of available statistics packages can be found by querying + // the "INFORMATION_SCHEMA.spanner_postgresql_STATISTICS" table. + .setOptimizerStatisticsPackage("latest") + .build()) + .build())) { + while (resultSet.next()) { + System.out.printf( + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2)); + } + } + } + // [END spanner_postgresql_query_with_query_options] + + // [START spanner_postgresql_list_backup_operations] + static void listBackupOperations(InstanceAdminClient instanceAdminClient, DatabaseId databaseId) { + Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); + // Get create backup operations for the sample database. + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); + String filter = + String.format( + "(metadata.database:%s) AND " + + "(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.CreateBackupMetadata) AND " + + "(metadata.progress.start_time > \"%s\")", + databaseId.getName(), last24Hours); + Page operations = instance + .listBackupOperations(Options.filter(filter)); + for (Operation op : operations.iterateAll()) { + try { + CreateBackupMetadata metadata = op.getMetadata().unpack(CreateBackupMetadata.class); + System.out.println( + String.format( + "Backup %s on database %s pending: %d%% complete", + metadata.getName(), + metadata.getDatabase(), + metadata.getProgress().getProgressPercent())); + } catch (InvalidProtocolBufferException e) { + // The returned operation does not contain CreateBackupMetadata. + System.err.println(e.getMessage()); + } + } + } + // [END spanner_postgresql_list_backup_operations] + + // [START spanner_postgresql_list_database_operations] + static void listDatabaseOperations( + InstanceAdminClient instanceAdminClient, + DatabaseAdminClient dbAdminClient, + InstanceId instanceId) { + Instance instance = instanceAdminClient.getInstance(instanceId.getInstance()); + // Get optimize restored database operations. + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); + String filter = String.format("(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata) AND " + + "(metadata.progress.start_time > \"%s\")", last24Hours); + for (Operation op : instance.listDatabaseOperations(Options.filter(filter)).iterateAll()) { + try { + OptimizeRestoredDatabaseMetadata metadata = + op.getMetadata().unpack(OptimizeRestoredDatabaseMetadata.class); + System.out.println(String.format( + "Database %s restored from backup is %d%% optimized", + metadata.getName(), + metadata.getProgress().getProgressPercent())); + } catch (InvalidProtocolBufferException e) { + // The returned operation does not contain OptimizeRestoredDatabaseMetadata. + System.err.println(e.getMessage()); + } + } + } + // [END spanner_postgresql_list_database_operations] + + static void run( + DatabaseClient dbClient, + DatabaseAdminClient dbAdminClient, + InstanceAdminClient instanceAdminClient, + String command, + DatabaseId database) { + switch (command) { + case "createdatabase": + createPostgreSqlDatabase(dbAdminClient, database); + break; + case "write": + writeExampleData(dbClient); + break; + case "delete": + deleteExampleData(dbClient); + break; + case "query": + query(dbClient); + break; + case "read": + read(dbClient); + break; + case "addmarketingbudget": + addMarketingBudget(dbAdminClient, database); + break; + case "update": + update(dbClient); + break; + case "writetransaction": + writeWithTransaction(dbClient); + break; + case "querymarketingbudget": + queryMarketingBudget(dbClient); + break; + case "addindex": + addIndex(dbAdminClient, database); + break; + case "readindex": + readUsingIndex(dbClient); + break; + case "addstoringindex": + addStoringIndex(dbAdminClient, database); + break; + case "readstoringindex": + readStoringIndex(dbClient); + break; + case "readonlytransaction": + readOnlyTransaction(dbClient); + break; + case "querysingerstable": + querySingersTable(dbClient); + break; + case "writeusingdml": + writeUsingDml(dbClient); + break; + case "querywithparameter": + queryWithParameter(dbClient); + break; + case "writewithtransactionusingdml": + writeWithTransactionUsingDml(dbClient); + break; + case "createtableusingddl": + createTableUsingDdl(dbAdminClient, database); + break; + case "readstaledata": + readStaleData(dbClient); + break; + case "addlastupdatetimestampcolumn": + addLastUpdateTimestampColumn(dbAdminClient, database); + break; + case "updatewithtimestamp": + updateWithTimestamp(dbClient); + break; + case "querywithtimestamp": + queryMarketingBudgetWithTimestamp(dbClient); + break; + case "createtablewithtimestamp": + createTableWithTimestamp(dbAdminClient, database); + break; + case "writewithtimestamp": + writeExampleDataWithTimestamp(dbClient); + break; + case "queryperformancestable": + queryPerformancesTable(dbClient); + break; + case "insertusingdml": + insertUsingDml(dbClient); + break; + case "updateusingdml": + updateUsingDml(dbClient); + break; + case "deleteusingdml": + deleteUsingDml(dbClient); + break; + case "writeandreadusingdml": + writeAndReadUsingDml(dbClient); + break; + case "updateusingpartitioneddml": + updateUsingPartitionedDml(dbClient); + break; + case "deleteusingpartitioneddml": + deleteUsingPartitionedDml(dbClient); + break; + case "updateusingbatchdml": + updateUsingBatchDml(dbClient); + break; + case "createtablewithdatatypes": + createTableWithDatatypes(dbAdminClient, database); + break; + case "writedatatypesdata": + writeDatatypesData(dbClient); + break; + case "querywithbool": + queryWithBool(dbClient); + break; + case "querywithbytes": + queryWithBytes(dbClient); + break; + case "querywithfloat": + queryWithFloat(dbClient); + break; + case "querywithint": + queryWithInt(dbClient); + break; + case "querywithstring": + queryWithString(dbClient); + break; + case "querywithtimestampparameter": + queryWithTimestampParameter(dbClient); + break; + case "querywithnumeric": + queryWithNumeric(dbClient); + break; + case "clientwithqueryoptions": + clientWithQueryOptions(database); + break; + case "querywithqueryoptions": + queryWithQueryOptions(dbClient); + break; + case "listbackupoperations": + listBackupOperations(instanceAdminClient, database); + break; + case "listdatabaseoperations": + listDatabaseOperations(instanceAdminClient, dbAdminClient, database.getInstanceId()); + break; + default: + printUsageAndExit(); + } + } + + static void printUsageAndExit() { + System.err.println("Usage:"); + System.err.println(" PgSpannerExample "); + System.err.println(); + System.err.println("Examples:"); + System.err.println(" PgSpannerExample 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) { + if (args.length != 3) { + printUsageAndExit(); + } + // [START spanner_init_client] + SpannerOptions options = SpannerOptions.newBuilder().build(); + Spanner spanner = options.getService(); + try { + // [END spanner_init_client] + String command = args[0]; + DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]); + + // This will return the default project id based on the environment. + String clientProject = spanner.getOptions().getProjectId(); + if (!db.getInstanceId().getProject().equals(clientProject)) { + System.err.println( + "Invalid project specified. Project in the database id should match the" + + "project name set in the environment variable GOOGLE_CLOUD_PROJECT. Expected: " + + clientProject); + printUsageAndExit(); + } + // [START spanner_init_client] + DatabaseClient dbClient = spanner.getDatabaseClient(db); + DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); + // [END spanner_init_client] + + // Use client here... + run(dbClient, dbAdminClient, instanceAdminClient, command, db); + // [START spanner_init_client] + } finally { + spanner.close(); + } + // [END spanner_init_client] + System.out.println("Closed client"); + } + + /** Class to contain singer sample data. */ + static class Singer { + + final long singerId; + final String firstName; + final String lastName; + + Singer(long singerId, String firstName, String lastName) { + this.singerId = singerId; + this.firstName = firstName; + this.lastName = lastName; + } + } + + /** Class to contain album sample data. */ + static class Album { + + final long singerId; + final long albumId; + final String albumTitle; + + Album(long singerId, long albumId, String albumTitle) { + this.singerId = singerId; + this.albumId = albumId; + this.albumTitle = albumTitle; + } + } +} diff --git a/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java new file mode 100644 index 00000000000..370b4924751 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java @@ -0,0 +1,92 @@ +/* + * 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 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.encryption.EncryptionConfigs; +import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata; +import java.util.concurrent.ExecutionException; + +public class RestoreBackupWithEncryptionKey { + + static void restoreBackupWithEncryptionKey() { + // 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 (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + restoreBackupWithEncryptionKey( + adminClient, + projectId, + instanceId, + backupId, + databaseId, + kmsKeyName); + } + } + + 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); + + Database database; + try { + System.out.println("Waiting for operation to complete..."); + database = operation.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().getSourceDatabase(), + database.getId(), + database.getRestoreInfo().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 00000000000..6371b314d2f --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java @@ -0,0 +1,2224 @@ +/* + * 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.paging.Page; +import com.google.api.gax.retrying.RetryingFuture; +import com.google.api.gax.rpc.StatusCode; +import com.google.cloud.ByteArray; +import com.google.cloud.Date; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Instance; +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; +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.instance.v1.InstanceAdminClient; +import com.google.common.io.BaseEncoding; +import com.google.longrunning.Operation; +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.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.OptimizeRestoredDatabaseMetadata; +import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata; +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.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import org.threeten.bp.Duration; +import org.threeten.bp.LocalDate; +import org.threeten.bp.LocalDateTime; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.temporal.ChronoField; +import org.threeten.bp.temporal.TemporalUnit; + +/** + * 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; + } + } + + /** 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( + 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, 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. + op.get(); + System.out.println("Created Venues table in database: [" + id + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_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(); + Backup backup = Backup.newBuilder() + .setName(BackupName.of(projectId, instanceId, backupId).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 (SpannerException) e.getCause(); + } 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(), + LocalDateTime.ofEpochSecond( + backup.getCreateTime().getSeconds(), + backup.getCreateTime().getNanos(), + OffsetDateTime.now().getOffset()), + LocalDateTime.ofEpochSecond( + backup.getVersionTime().getSeconds(), + backup.getVersionTime().getNanos(), + OffsetDateTime.now().getOffset()) + )); + } + // [END spanner_create_backup] + + // [START spanner_cancel_backup_create] + static void cancelCreateBackup( + DatabaseAdminClient dbAdminClient, DatabaseId databaseId, BackupId 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(); + try { + // Try to cancel the backup operation. + 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. + 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. + 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 [" + backup.getId() + "] 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( + 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)", + databaseId.getName()); + Page createBackupOperations = instance.listBackupOperations( + Options.filter(filter)); + System.out.println("Create Backup Operations:"); + for (Operation op : createBackupOperations.iterateAll()) { + try { + CreateBackupMetadata metadata = op.getMetadata().unpack(CreateBackupMetadata.class); + System.out.println( + 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)", + backupId.getName()); + Page copyBackupOperations = instance.listBackupOperations(Options.filter(filter)); + System.out.println("Copy Backup Operations:"); + for (Operation op : copyBackupOperations.iterateAll()) { + try { + CopyBackupMetadata copyBackupMetadata = + op.getMetadata().unpack(CopyBackupMetadata.class); + System.out.println( + String.format( + "Copy Backup %s on backup %s pending: %d%% complete", + copyBackupMetadata.getName(), + copyBackupMetadata.getSourceBackup(), + copyBackupMetadata.getProgress().getProgressPercent())); + } catch (InvalidProtocolBufferException e) { + // The returned operation does not contain CopyBackupMetadata. + System.err.println(e.getMessage()); + } + } + } + // [END spanner_list_backup_operations] + + // [START spanner_list_database_operations] + static void listDatabaseOperations( + InstanceAdminClient instanceAdminClient, + DatabaseAdminClient dbAdminClient, + InstanceId instanceId) { + Instance instance = instanceAdminClient.getInstance(instanceId.getInstance()); + // Get optimize restored database operations. + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); + String filter = String.format("(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata) AND " + + "(metadata.progress.start_time > \"%s\")", last24Hours); + for (Operation op : instance.listDatabaseOperations(Options.filter(filter)).iterateAll()) { + try { + OptimizeRestoredDatabaseMetadata metadata = + op.getMetadata().unpack(OptimizeRestoredDatabaseMetadata.class); + System.out.println(String.format( + "Database %s restored from backup is %d%% optimized", + metadata.getName(), + metadata.getProgress().getProgressPercent())); + } catch (InvalidProtocolBufferException e) { + // The returned operation does not contain OptimizeRestoredDatabaseMetadata. + System.err.println(e.getMessage()); + } + } + } + // [END spanner_list_database_operations] + + // [START spanner_list_backups] + static void listBackups( + InstanceAdminClient instanceAdminClient, DatabaseId databaseId, BackupId backupId) { + Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); + // List all backups. + System.out.println("All backups:"); + 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.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.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. + 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. + System.out.println("All backups with size greater than 100 bytes:"); + 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. + 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())); + 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:"); + Page page = instance.listBackups(Options.pageSize(10)); + while (true) { + for (Backup backup : page.getValues()) { + System.out.println(backup); + } + if (!page.hasNextPage()) { + break; + } + page = page.getNextPage(); + } + } + // [END spanner_list_backups] + + // [START spanner_restore_backup] + static void restoreBackup( + DatabaseAdminClient dbAdminClient, + 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.getId().toString(), + restoreToDatabase.toString())); + try { + OperationFuture op = backup.restore(restoreToDatabase); + // 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()); + System.out.println( + "Restored database [" + + restore.getSourceDatabase().getName() + + "] from [" + + restore.getBackup().getName() + + "] with version time [" + versionTime + "]"); + } 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, BackupId backupId) { + // Get current backup metadata. + 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 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 + expireTime = expireTime.compareTo(backup.getMaxExpireTime()) + < 0 ? expireTime : backup.getMaxExpireTime(); + int timeDiff = expireTime.compareTo(backup.getExpireTime()); + Timestamp newExpireTime = (timeDiff < 0) ? expireTime : backup.getExpireTime(); + + System.out.println(String.format( + "Updating expire time of backup [%s] to %s...", + backupId.toString(), + LocalDateTime.ofEpochSecond( + expireTime.getSeconds(), + expireTime.getNanos(), + OffsetDateTime.now().getOffset()).toString())); + + // Update expire time. + 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, BackupId backupId) { + Backup backup = dbAdminClient.newBackupBuilder(backupId).build(); + // Delete the backup. + System.out.println("Deleting backup [" + backupId + "]..."); + backup.delete(); + // 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 + "]"); + } + } + // [END spanner_delete_backup] + + static void run( + DatabaseClient dbClient, + DatabaseAdminClient dbAdminClient, + InstanceAdminClient instanceAdminClient, + 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, database); + break; + case "update": + update(dbClient); + break; + case "writetransaction": + writeWithTransaction(dbClient); + break; + case "querymarketingbudget": + queryMarketingBudget(dbClient); + break; + case "addindex": + addIndex(dbAdminClient, database); + break; + case "readindex": + readUsingIndex(dbClient); + break; + case "queryindex": + queryUsingIndex(dbClient); + break; + case "addstoringindex": + addStoringIndex(dbAdminClient, database); + break; + case "readstoringindex": + readStoringIndex(dbClient); + break; + case "readonlytransaction": + readOnlyTransaction(dbClient); + break; + case "readstaledata": + readStaleData(dbClient); + break; + case "addcommittimestamp": + addCommitTimestamp(dbAdminClient, database); + 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, database); + 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, + BackupId.of(backup.getInstanceId(), backup.getBackup() + "_cancel")); + break; + case "listbackupoperations": + listBackupOperations(instanceAdminClient, database, backup); + break; + case "listdatabaseoperations": + listDatabaseOperations(instanceAdminClient, dbAdminClient, database.getInstanceId()); + break; + case "listbackups": + listBackups(instanceAdminClient, database, backup); + break; + case "restorebackup": + restoreBackup( + dbAdminClient, + backup, + database, + DatabaseId.of(database.getInstanceId(), createRestoredSampleDbId(database))); + break; + case "updatebackup": + updateBackup(dbAdminClient, backup); + break; + case "deletebackup": + deleteBackup(dbAdminClient, backup); + 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(); + try { + String command = args[0]; + DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]); + DatabaseName databaseName = DatabaseName.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 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) { + backupName = args[3]; + } + + // [START init_client] + DatabaseClient dbClient = spanner.getDatabaseClient(db); + DatabaseAdminClient dbAdminClient = DatabaseAdminClient.create(); + InstanceAdminClient instanceAdminClient = InstanceAdminClient.create(); + + // Use client here... + // [END init_client] + + run(dbClient, dbAdminClient, instanceAdminClient, command, db, databaseName, backup); + // [START init_client] + } finally { + spanner.close(); + } + // [END init_client] + System.out.println("Closed client"); + } +} From c9d60dfad1f28d4a3aa41380de18042c56752ca4 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Wed, 10 Jan 2024 10:49:11 +0530 Subject: [PATCH 04/30] chore: fix code. --- .../admin/generated/SpannerSample.java | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) 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 6371b314d2f..5b3acefa3b9 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 @@ -28,6 +28,7 @@ import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.Instance; import com.google.cloud.spanner.InstanceId; import com.google.cloud.spanner.Key; @@ -1876,18 +1877,24 @@ static void updateBackup(DatabaseAdminClient dbAdminClient, BackupId 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 (SpannerException e) { + if (e.getErrorCode() == ErrorCode.NOT_FOUND) { + System.out.println("Delete backup [" + backupId + "] failed"); + throw new RuntimeException("Delete backup [" + backupId + "] failed"); + } + throw e; } + System.out.println("Deleted backup [" + backupId + "]"); } // [END spanner_delete_backup] @@ -1916,7 +1923,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); @@ -1928,7 +1936,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); @@ -1937,7 +1946,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); @@ -1949,7 +1959,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); @@ -2184,7 +2195,6 @@ public static void main(String[] args) throws Exception { try { String command = args[0]; DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]); - DatabaseName databaseName = DatabaseName.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(); @@ -2196,13 +2206,12 @@ public static void main(String[] args) throws Exception { printUsageAndExit(); } // Generate a backup id for the sample database. - String backupName = + String backupId = 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) { - backupName = args[3]; + backupId = args[3]; } // [START init_client] @@ -2213,7 +2222,7 @@ public static void main(String[] args) throws Exception { // Use client here... // [END init_client] - run(dbClient, dbAdminClient, instanceAdminClient, command, db, databaseName, backup); + run(dbClient, dbAdminClient, instanceAdminClient, command, db, backupId); // [START init_client] } finally { spanner.close(); From 8989e7c79f7df9cd01662cd11d971a9dbd1a552e Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Wed, 10 Jan 2024 19:34:45 +0530 Subject: [PATCH 05/30] chore: fix code. --- .../admin/generated/EncryptionKeyIT.java | 125 ++++ .../admin/generated/PgSpannerSampleIT.java | 304 +++++++++ .../admin/generated/SpannerSampleIT.java | 592 ++++++++++++++++++ 3 files changed, 1021 insertions(+) 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/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 00000000000..03aa10a55f3 --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/admin/generated/EncryptionKeyIT.java @@ -0,0 +1,125 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +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 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 CreateDatabaseWithEncryptionKey}, {@link + * CreateBackupWithEncryptionKey} and {@link RestoreBackupWithEncryptionKey} + */ +@RunWith(JUnit4.class) +@Ignore +public class EncryptionKeyIT extends SampleTestBase { + + 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(() -> + 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( + 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 00000000000..796df092c80 --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/admin/generated/PgSpannerSampleIT.java @@ -0,0 +1,304 @@ +/* + * Copyright 2022 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Unit tests for {@code PgSpannerSample} + */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:abbreviationaswordinname") +public class PgSpannerSampleIT { + private static final int DBID_LENGTH = 20; + // The instance needs to exist for tests to pass. + private static final String instanceId = System.getProperty("spanner.test.instance"); + private static final String baseDbId = System.getProperty("spanner.sample.database"); + private static final String databaseId = formatForTest(baseDbId); + private static final String encryptedDatabaseId = formatForTest(baseDbId); + private static final String encryptedBackupId = formatForTest(baseDbId); + private static final String encryptedRestoreId = formatForTest(baseDbId); + static Spanner spanner; + static DatabaseId dbId; + static DatabaseAdminClient dbClient; + + @BeforeClass + public static void setUp() { + SpannerOptions options = + SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build(); + spanner = options.getService(); + dbClient = spanner.getDatabaseAdminClient(); + dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); + // Delete stale test databases that have been created earlier by this test, but not deleted. + deleteStaleTestDatabases(); + } + + static void deleteStaleTestDatabases() { + Timestamp now = Timestamp.now(); + Pattern samplePattern = getTestDbIdPattern(PgSpannerSampleIT.baseDbId); + Pattern restoredPattern = getTestDbIdPattern("restored"); + for (Database db : dbClient.listDatabases(PgSpannerSampleIT.instanceId).iterateAll()) { + if (TimeUnit.HOURS.convert(now.getSeconds() - db.getCreateTime().getSeconds(), + TimeUnit.SECONDS) > 24) { + if (db.getId().getDatabase().length() >= DBID_LENGTH) { + if (samplePattern.matcher(toComparableId(PgSpannerSampleIT.baseDbId, + db.getId().getDatabase())).matches()) { + db.drop(); + } + if (restoredPattern.matcher(toComparableId("restored", db.getId().getDatabase())) + .matches()) { + db.drop(); + } + } + } + } + } + + @AfterClass + public static void tearDown() { + dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); + dbClient.dropDatabase( + dbId.getInstanceId().getInstance(), SpannerSample.createRestoredSampleDbId(dbId)); + dbClient.dropDatabase(instanceId, encryptedDatabaseId); + dbClient.dropDatabase(instanceId, encryptedRestoreId); + dbClient.deleteBackup(instanceId, encryptedBackupId); + spanner.close(); + } + + private static String toComparableId(String baseId, String existingId) { + String zeroUuid = "00000000-0000-0000-0000-0000-00000000"; + int shouldBeLength = (baseId + "-" + zeroUuid).length(); + int missingLength = shouldBeLength - existingId.length(); + return existingId + zeroUuid.substring(zeroUuid.length() - missingLength); + } + + private static Pattern getTestDbIdPattern(String baseDbId) { + return Pattern.compile( + baseDbId + "-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{8}", + Pattern.CASE_INSENSITIVE); + } + + static String formatForTest(String name) { + return name + "-" + UUID.randomUUID().toString().substring(0, DBID_LENGTH); + } + + private String runSample(String command) { + final PrintStream stdOut = System.out; + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + final PrintStream out = new PrintStream(bout); + System.setOut(out); + System.out.println(instanceId + ":" + databaseId); + PgSpannerSample.main(new String[]{command, instanceId, databaseId}); + System.setOut(stdOut); + return bout.toString(); + } + + @Test + public void testSample() throws Exception { + assertThat(instanceId).isNotNull(); + assertThat(databaseId).isNotNull(); + + System.out.println("Create Database ..."); + String out = runSample("createpgdatabase"); + assertThat(out).contains("Created database"); + assertThat(out).contains(dbId.getName()); + + System.out.println("Create sample tables Singers and Albums ..."); + runSample("createtableusingddl"); + + System.out.println("Write data to sample tables ..."); + runSample("write"); + + System.out.println("Read data from sample tables ..."); + out = runSample("read"); + assertThat(out).contains("1 1 Total Junk"); + + System.out.println("Write data using DML to sample table ..."); + runSample("writeusingdml"); + System.out.println("Query Singers table ..."); + out = runSample("querysingerstable"); + assertThat(out).contains("Melissa Garcia"); + out = runSample("query"); + assertThat(out).contains("1 1 Total Junk"); + out = runSample("querywithparameter"); + assertThat(out).contains("12 Melissa Garcia"); + + System.out.println("Add column marketing budget ..."); + runSample("addmarketingbudget"); + + // wait for 15 seconds to elapse and then run an update, and query for stale data + long lastUpdateDataTimeInMillis = System.currentTimeMillis(); + while (System.currentTimeMillis() < lastUpdateDataTimeInMillis + 16000) { + Thread.sleep(1000); + } + System.out.println("Write data to marketing budget ..."); + runSample("update"); + + System.out.println("Query marketing budget ..."); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 100000"); + assertThat(out).contains("2 2 500000"); + + System.out.println("Write with transaction using dml..."); + runSample("writewithtransactionusingdml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 300000"); + assertThat(out).contains("1 1 300000"); + + System.out.println("Add index ..."); + runSample("addindex"); + + System.out.println("Read index ..."); + out = runSample("readindex"); + assertThat(out).contains("Go, Go, Go"); + assertThat(out).contains("Forever Hold Your Peace"); + assertThat(out).contains("Green"); + + System.out.println("Add Storing index ..."); + runSample("addstoringindex"); + + System.out.println("Read storing index ..."); + out = runSample("readstoringindex"); + assertThat(out).contains("300000"); + + System.out.println("Read only transaction ..."); + out = runSample("readonlytransaction"); + assertThat(out.replaceAll("[\r\n]+", " ")) + .containsMatch("(Total Junk.*){2}"); + + System.out.println("Add Timestamp column ..."); + out = runSample("addlastupdatetimestampcolumn"); + assertThat(out).contains("Added LastUpdateTime as a timestamp column"); + + System.out.println("Update values in Timestamp column ..."); + runSample("updatewithtimestamp"); + out = runSample("querywithtimestamp"); + assertThat(out).contains("1 1 1000000"); + assertThat(out).contains("2 2 750000"); + + System.out.println("Create table with Timestamp column ..."); + out = runSample("createtablewithtimestamp"); + assertThat(out).contains("Created Performances table in database"); + + System.out.println("Write with Timestamp ..."); + runSample("writewithtimestamp"); + out = runSample("queryperformancestable"); + assertThat(out).contains("1 4 11000"); + assertThat(out).contains("1 19 15000"); + assertThat(out).contains("2 42 7000"); + + System.out.println("Write using DML ..."); + runSample("insertusingdml"); + out = runSample("querysingerstable"); + assertThat(out).contains("Virginia Watson"); + + System.out.println("Update using DML ..."); + runSample("updateusingdml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 2000000"); + + System.out.println("Delete using DML ..."); + runSample("deleteusingdml"); + out = runSample("querysingerstable"); + assertThat(out).doesNotContain("Alice Trentor"); + + System.out.println("Write and Read using DML ..."); + out = runSample("writeandreadusingdml"); + assertThat(out).contains("Timothy Campbell"); + + System.out.println("Update using partitioned DML ..."); + runSample("updateusingpartitioneddml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("2 2 100000"); + assertThat(out).contains("1 1 2000000"); + + System.out.println("Delete using Partitioned DML ..."); + runSample("deleteusingpartitioneddml"); + out = runSample("querysingerstable"); + assertThat(out).doesNotContain("Timothy Grant"); + assertThat(out).doesNotContain("Melissa Garcia"); + assertThat(out).doesNotContain("Russell Morales"); + assertThat(out).doesNotContain("Jacqueline Long"); + assertThat(out).doesNotContain("Dylan Shaw"); + + System.out.println("Update in Batch using DML ..."); + out = runSample("updateusingbatchdml"); + assertThat(out).contains("1 record updated by stmt 0"); + assertThat(out).contains("1 record updated by stmt 1"); + + System.out.println("Create table with data types ..."); + out = runSample("createtablewithdatatypes"); + assertThat(out).contains("Created Venues table in database"); + + System.out.println("Write into table and Query Boolean Type ..."); + runSample("writedatatypesdata"); + out = runSample("querywithbool"); + assertThat(out).contains("19 Venue 19 true"); + + System.out.println("Query with Bytes ..."); + out = runSample("querywithbytes"); + assertThat(out).contains("4 Venue 4"); + + System.out.println("Query with Float ..."); + out = runSample("querywithfloat"); + assertThat(out).contains("4 Venue 4 0.8"); + assertThat(out).contains("19 Venue 19 0.9"); + + System.out.println("Query with Int ..."); + out = runSample("querywithint"); + assertThat(out).contains("19 Venue 19 6300"); + assertThat(out).contains("42 Venue 42 3000"); + + System.out.println("Query with String ..."); + out = runSample("querywithstring"); + assertThat(out).contains("42 Venue 42"); + + System.out.println("Query with Timestamp parameter ..."); + out = runSample("querywithtimestampparameter"); + assertThat(out).contains("4 Venue 4"); + assertThat(out).contains("19 Venue 19"); + assertThat(out).contains("42 Venue 42"); + + System.out.println("Query with Numeric Type ..."); + out = runSample("querywithnumeric"); + assertThat(out).contains("19 Venue 19 1200100"); + assertThat(out).contains("42 Venue 42 390650.99"); + + System.out.println("Query options ..."); + out = runSample("clientwithqueryoptions"); + assertThat(out).contains("1 1 Total Junk"); + out = runSample("querywithqueryoptions"); + assertThat(out).contains("1 1 Total Junk"); + } +} diff --git a/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java new file mode 100644 index 00000000000..72e8c36d76c --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java @@ -0,0 +1,592 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Backup; +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; + } + } +} From 4e8467111111d154fec8156441d4c5cb21c11fef Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Thu, 11 Jan 2024 10:28:48 +0530 Subject: [PATCH 06/30] chore: fix code. --- .../admin/generated/SpannerSample.java | 203 ++++++++++-------- .../admin/generated/EncryptionKeyIT.java | 8 +- .../admin/generated/PgSpannerSampleIT.java | 2 +- .../admin/generated/SpannerSampleIT.java | 10 +- 4 files changed, 128 insertions(+), 95 deletions(-) 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 5b3acefa3b9..6fdd20acb0e 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 @@ -29,8 +29,6 @@ import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ErrorCode; -import com.google.cloud.spanner.Instance; -import com.google.cloud.spanner.InstanceId; import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; @@ -50,6 +48,8 @@ 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.ListDatabaseOperationsPagedResponse; import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient; import com.google.common.io.BaseEncoding; import com.google.longrunning.Operation; @@ -62,9 +62,13 @@ 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.ListDatabaseOperationsResponse; 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.instance.v1.Instance; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; import java.math.BigDecimal; import java.time.Instant; @@ -73,12 +77,10 @@ import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import org.threeten.bp.Duration; import org.threeten.bp.LocalDate; import org.threeten.bp.LocalDateTime; import org.threeten.bp.OffsetDateTime; import org.threeten.bp.temporal.ChronoField; -import org.threeten.bp.temporal.TemporalUnit; /** * Example code for using the Cloud Spanner API. This example demonstrates all the common operations @@ -1242,30 +1244,25 @@ 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(); @@ -1602,28 +1599,33 @@ static void createBackup(DatabaseAdminClient dbAdminClient, String projectId, St // [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 { - // Try to cancel the backup operation. - System.out.println("Cancelling create backup operation for [" + backup.getId() + "]..."); - dbAdminClient.cancelOperation(op.getName()); + // Start the creation of a backup. + System.out.println("Creating backup [" + backupId + "]..."); + OperationFuture op = dbAdminClient.createBackupAsync( + InstanceName.of(projectId, instanceId), backup, backupId); + // 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(); + + // Try to cancel the backup operation. + System.out.println("Cancelling create backup operation for [" + backupId + "]..."); + pollingFuture.cancel(true); + // Wait for the operation to finish. // isDone will return true when the operation is complete, regardless of whether it was // successful or not. @@ -1633,11 +1635,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()); @@ -1649,20 +1651,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()); + DatabaseAdminClient databaseAdminClient, + String projectId, String instanceId, + String databaseId, String backupId) { + com.google.spanner.admin.instance.v1.InstanceName instanceName = com.google.spanner.admin.instance.v1.InstanceName.of(projectId, instanceId) // 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)", - databaseId.getName()); - Page createBackupOperations = instance.listBackupOperations( - Options.filter(filter)); + 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 { @@ -1679,13 +1683,15 @@ static void listBackupOperations( } } // Get copy backup operations for the sample database. - filter = - String.format( + 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)); + 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 { @@ -1707,18 +1713,22 @@ 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); + Timestamp last24Hours = Timestamp.newBuilder().setSeconds((TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(new java.sql.Timestamp( + System.currentTimeMillis()).getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS))).build(); 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); @@ -1736,46 +1746,60 @@ 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( + Timestamp expireTime = Timestamp.newBuilder().setSeconds((TimeUnit.SECONDS.convert( + TimeUnit.SECONDS.convert(new java.sql.Timestamp( + System.currentTimeMillis()).getSeconds(), TimeUnit.SECONDS) + TimeUnit.DAYS.toSeconds(30), + TimeUnit.HOURS))).build(); + + Timestamp expireTime = Timestamp.ofTimeMicroseconds(TimeUnit.SECONDS.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()) { + listBackupsRequest = + ListBackupsRequest.newBuilder().setParent(instanceName.toString()) + .setFilter(String.format("expire_time < \"%s\"", expireTime.toString())).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); } @@ -2033,7 +2057,8 @@ 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); @@ -2079,7 +2104,9 @@ static void run( case "cancelcreatebackup": cancelCreateBackup( dbAdminClient, - database, + database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase(), + backupId BackupId.of(backup.getInstanceId(), backup.getBackup() + "_cancel")); break; case "listbackupoperations": @@ -2099,10 +2126,12 @@ static void run( DatabaseId.of(database.getInstanceId(), createRestoredSampleDbId(database))); 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(); 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 index 03aa10a55f3..3ffe27f5152 100644 --- 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 @@ -14,10 +14,12 @@ * limitations under the License. */ -package com.example.spanner; +package com.example.spanner.admin.generated; 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; @@ -32,8 +34,8 @@ import org.junit.runners.JUnit4; /** - * Integration tests for: {@link CreateDatabaseWithEncryptionKey}, {@link - * CreateBackupWithEncryptionKey} and {@link RestoreBackupWithEncryptionKey} + * Integration tests for: {@link com.example.spanner.CreateDatabaseWithEncryptionKey}, {@link + * com.example.spanner.CreateBackupWithEncryptionKey} and {@link com.example.spanner.RestoreBackupWithEncryptionKey} */ @RunWith(JUnit4.class) @Ignore 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 index 796df092c80..1ebc838947d 100644 --- 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 @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.spanner; +package com.example.spanner.admin.generated; import static com.google.common.truth.Truth.assertThat; 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 index 72e8c36d76c..b0d4f3d9514 100644 --- 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 @@ -14,11 +14,13 @@ * limitations under the License. */ -package com.example.spanner; +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.example.spanner.SpannerSample; import com.google.cloud.Timestamp; import com.google.cloud.spanner.Backup; import com.google.cloud.spanner.BackupId; @@ -83,7 +85,7 @@ private String runSample(String command) throws Exception { ByteArrayOutputStream bout = new ByteArrayOutputStream(); PrintStream out = new PrintStream(bout); System.setOut(out); - SpannerSample.main(new String[] {command, instanceId, databaseId}); + com.example.spanner.SpannerSample.main(new String[] {command, instanceId, databaseId}); System.setOut(stdOut); return bout.toString(); } @@ -428,7 +430,7 @@ public void testSample() throws Exception { "Database %s restored from backup", DatabaseId.of( dbId.getInstanceId(), - SpannerSample.createRestoredSampleDbId(dbId)) + com.example.spanner.SpannerSample.createRestoredSampleDbId(dbId)) .getName())); } @@ -532,7 +534,7 @@ public void testCreateInstanceSample() { String out = runSampleRunnable(() -> { try { - CreateInstanceExample.createInstance( + com.example.spanner.CreateInstanceExample.createInstance( dbId.getInstanceId().getProject(), instanceId); } finally { spanner.getInstanceAdminClient().deleteInstance(instanceId); From 3c0d0f658b0dc9e9efc15179d559358bb431174e Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Tue, 13 Feb 2024 17:43:09 +0530 Subject: [PATCH 07/30] chore: fix error. --- .../com/example/spanner/admin/generated/SpannerSample.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6fdd20acb0e..c2b3621ea89 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 @@ -1654,7 +1654,7 @@ static void listBackupOperations( DatabaseAdminClient databaseAdminClient, String projectId, String instanceId, String databaseId, String backupId) { - com.google.spanner.admin.instance.v1.InstanceName instanceName = com.google.spanner.admin.instance.v1.InstanceName.of(projectId, instanceId) + InstanceName instanceName = InstanceName.of(projectId, instanceId); // Get create backup operations for the sample database. String filter = String.format( @@ -1748,7 +1748,7 @@ static void listDatabaseOperations( static void listBackups( DatabaseAdminClient dbAdminClient, String projectId, String instanceId, String databaseId, String backupId) { - InstanceName instanceName = InstanceName.of(projectId, instanceId) + InstanceName instanceName = InstanceName.of(projectId, instanceId); // List all backups. System.out.println("All backups:"); for (Backup backup : dbAdminClient.listBackups( From 8e51bdeff15d76ecadcf476525222d025a5cc83b Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Thu, 15 Feb 2024 17:02:10 +0530 Subject: [PATCH 08/30] fix: all compilation errors. --- .../admin/generated/SpannerSample.java | 237 ++++++++++-------- .../admin/generated/SpannerSampleIT.java | 7 +- 2 files changed, 135 insertions(+), 109 deletions(-) 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 c2b3621ea89..59672690a5b 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 @@ -20,12 +20,10 @@ 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.StatusCode; import com.google.cloud.ByteArray; import com.google.cloud.Date; -import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ErrorCode; @@ -33,9 +31,7 @@ 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; @@ -49,13 +45,19 @@ 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.cloud.spanner.admin.instance.v1.InstanceAdminClient; +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.protobuf.util.Timestamps; 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; @@ -65,10 +67,10 @@ 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.ListDatabaseOperationsResponse; import com.google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata; import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata; -import com.google.spanner.admin.instance.v1.Instance; +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; @@ -100,7 +102,9 @@ */ public class SpannerSample { - /** Class to contain singer sample data. */ + /** + * Class to contain singer sample data. + */ static class Singer { final long singerId; @@ -114,7 +118,9 @@ static class Singer { } } - /** Class to contain album sample data. */ + /** + * Class to contain album sample data. + */ static class Album { final long singerId; @@ -128,7 +134,9 @@ static class Album { } } - /** Class to contain performance sample data. */ + /** + * Class to contain performance sample data. + */ static class Performance { final long singerId; @@ -144,7 +152,9 @@ static class Performance { } } - /** Class to contain venue sample data. */ + /** + * Class to contain venue sample data. + */ static class Venue { final long venueId; @@ -182,7 +192,9 @@ static class Venue { } } - /** Get a database id to restore a backup to from the sample database id. */ + /** + * 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); @@ -317,7 +329,8 @@ 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, + DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. dbAdminClient.updateDatabaseDdlAsync( @@ -331,7 +344,8 @@ static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, Database + " 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() + "]"); + 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(); @@ -1175,18 +1189,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(); @@ -1244,7 +1258,8 @@ 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, + DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. dbAdminClient.updateDatabaseDdlAsync(databaseName, @@ -1593,7 +1608,7 @@ static void createBackup(DatabaseAdminClient dbAdminClient, String projectId, St backup.getVersionTime().getSeconds(), backup.getVersionTime().getNanos(), OffsetDateTime.now().getOffset()) - )); + )); } // [END spanner_create_backup] @@ -1611,7 +1626,6 @@ static void cancelCreateBackup( .setDatabase(DatabaseName.of(projectId, instanceId, databaseId).toString()) .setExpireTime(expireTime).build(); - try { // Start the creation of a backup. System.out.println("Creating backup [" + backupId + "]..."); @@ -1658,10 +1672,10 @@ static void listBackupOperations( // Get create backup 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()); + "(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(); @@ -1684,25 +1698,26 @@ 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)", + "(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); + 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()); @@ -1720,8 +1735,8 @@ static void listDatabaseOperations( System.currentTimeMillis()).getSeconds(), TimeUnit.SECONDS) - 24, TimeUnit.HOURS))).build(); String filter = String.format("(metadata.@type:type.googleapis.com/" - + "google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata) AND " - + "(metadata.progress.start_time > \"%s\")", last24Hours); + + "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( @@ -1760,7 +1775,8 @@ static void listBackups( 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(); + ListBackupsRequest.newBuilder().setParent(instanceName.toString()) + .setFilter(String.format("name:%s", backupId)).build(); for (Backup backup : dbAdminClient.listBackups(listBackupsRequest).iterateAll()) { System.out.println(backup); } @@ -1777,13 +1793,10 @@ static void listBackups( } // List all backups that expire before a certain time. - Timestamp expireTime = Timestamp.newBuilder().setSeconds((TimeUnit.SECONDS.convert( - TimeUnit.SECONDS.convert(new java.sql.Timestamp( - System.currentTimeMillis()).getSeconds(), TimeUnit.SECONDS) + TimeUnit.DAYS.toSeconds(30), - TimeUnit.HOURS))).build(); + Timestamp expireTime = + Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds(( + System.currentTimeMillis() + TimeUnit.DAYS.toMillis(30)))).build(); - Timestamp expireTime = Timestamp.ofTimeMicroseconds(TimeUnit.SECONDS.convert( - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(30), TimeUnit.MILLISECONDS)); System.out.println(String.format("All backups that expire before %s:", expireTime.toString())); listBackupsRequest = ListBackupsRequest.newBuilder().setParent(instanceName.toString()) @@ -1804,30 +1817,35 @@ static void listBackups( } // 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)); + Timestamp createTime = + Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds(( + System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)))).build(); 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] @@ -1835,31 +1853,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(DatabaseName.of(projectId, instanceId, restoreToDatabaseId).toString()) + .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() + + restoreInfo.getBackupInfo().getSourceDatabase() + "] 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) { @@ -1869,33 +1891,36 @@ 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()) - + TimeUnit.DAYS.toMicros(30L)); + Timestamp currentExpireTime = backup.getExpireTime(); + Timestamp newExpireTime = + Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds(( + TimeUnit.SECONDS.toMillis(currentExpireTime.getSeconds()) + + TimeUnit.NANOSECONDS.toMillis(currentExpireTime.getNanos()) + + TimeUnit.DAYS.toMillis(30)))).build(); + // 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 = + Timestamps.compare(newExpireTime, backup.getMaxExpireTime()) < 0 ? newExpireTime + : 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())); + newExpireTime.getSeconds(), newExpireTime.getNanos(), OffsetDateTime.now().getOffset()))); // Update expire time. - backup = backup.toBuilder().setExpireTime(expireTime).build(); - backup.updateExpireTime(); + backup = backup.toBuilder().setExpireTime(newExpireTime).build(); + dbAdminClient.updateBackup(backup, + FieldMask.newBuilder().addAllPaths(Lists.newArrayList("expire_time")).build()); System.out.println("Updated backup [" + backupId + "]"); } // [END spanner_update_backup] @@ -1925,7 +1950,6 @@ static void deleteBackup(DatabaseAdminClient dbAdminClient, static void run( DatabaseClient dbClient, DatabaseAdminClient dbAdminClient, - InstanceAdminClient instanceAdminClient, String command, DatabaseId database, String backupId) { @@ -1993,8 +2017,9 @@ static void run( queryMarketingBudgetWithTimestamp(dbClient); break; case "createtablewithtimestamp": - createTableWithTimestamp(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase())); + createTableWithTimestamp(dbAdminClient, + DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase())); break; case "writewithtimestamp": writeExampleDataWithTimestamp(dbClient); @@ -2057,8 +2082,9 @@ static void run( updateUsingBatchDml(dbClient); break; case "createtablewithdatatypes": - createTableWithDatatypes(dbAdminClient, DatabaseName.of(database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), database.getDatabase())); + createTableWithDatatypes(dbAdminClient, + DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase())); break; case "writedatatypesdata": writeDatatypesData(dbClient); @@ -2106,24 +2132,24 @@ static void run( dbAdminClient, database.getInstanceId().getProject(), database.getInstanceId().getInstance(), database.getDatabase(), - backupId - BackupId.of(backup.getInstanceId(), backup.getBackup() + "_cancel")); + 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, createRestoredSampleDbId(database)); break; case "updatebackup": updateBackup(dbAdminClient, database.getInstanceId().getProject(), @@ -2246,12 +2272,11 @@ public static void main(String[] args) throws Exception { // [START init_client] DatabaseClient dbClient = spanner.getDatabaseClient(db); DatabaseAdminClient dbAdminClient = DatabaseAdminClient.create(); - InstanceAdminClient instanceAdminClient = InstanceAdminClient.create(); // Use client here... // [END init_client] - run(dbClient, dbAdminClient, instanceAdminClient, command, db, backupId); + run(dbClient, dbAdminClient, command, db, backupId); // [START init_client] } finally { spanner.close(); 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 index b0d4f3d9514..32066811b55 100644 --- 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 @@ -20,7 +20,6 @@ import static org.junit.Assert.assertTrue; import com.example.spanner.SampleRunner; -import com.example.spanner.SpannerSample; import com.google.cloud.Timestamp; import com.google.cloud.spanner.Backup; import com.google.cloud.spanner.BackupId; @@ -41,6 +40,7 @@ import com.google.common.base.Preconditions; import com.google.common.util.concurrent.Uninterruptibles; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.PrintStream; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -430,7 +430,7 @@ public void testSample() throws Exception { "Database %s restored from backup", DatabaseId.of( dbId.getInstanceId(), - com.example.spanner.SpannerSample.createRestoredSampleDbId(dbId)) + com.example.spanner.admin.generated.SpannerSample.createRestoredSampleDbId(dbId)) .getName())); } @@ -534,8 +534,9 @@ public void testCreateInstanceSample() { String out = runSampleRunnable(() -> { try { - com.example.spanner.CreateInstanceExample.createInstance( + com.example.spanner.admin.generated.CreateInstanceExample.createInstance( dbId.getInstanceId().getProject(), instanceId); + } catch (IOException ex) { } finally { spanner.getInstanceAdminClient().deleteInstance(instanceId); } From fb9ad16d588b30530003ab5107e9dd56552deb59 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Thu, 15 Feb 2024 19:58:24 +0530 Subject: [PATCH 09/30] fix: more compilation issues. --- .../CreateBackupWithEncryptionKey.java | 5 - .../CreateDatabaseWithEncryptionKey.java | 65 ++- .../RestoreBackupWithEncryptionKey.java | 6 - .../admin/generated/SpannerSample.java | 1 - .../admin/generated/EncryptionKeyIT.java | 2 +- .../admin/generated/SpannerSampleIT.java | 379 +++++++++--------- 6 files changed, 232 insertions(+), 226 deletions(-) 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 890e0a614ad..c6bd1606bef 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 @@ -20,11 +20,6 @@ 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; 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 cd50f5ed6fd..72eb5476d7e 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,23 +18,21 @@ // [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.EncryptionConfig; +import com.google.spanner.admin.database.v1.InstanceName; +import com.google.spanner.admin.database.v1.Database; +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() { + static void createDatabaseWithEncryptionKey() throws IOException { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; String instanceId = "my-instance"; @@ -42,9 +40,7 @@ static void createDatabaseWithEncryptionKey() { String kmsKeyName = "projects/" + projectId + "/locations//keyRings//cryptoKeys/"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + try (DatabaseAdminClient adminClient = DatabaseAdminClient.create()) { createDatabaseWithEncryptionKey( adminClient, projectId, @@ -56,32 +52,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/admin/generated/RestoreBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java index 370b4924751..432194104b8 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 @@ -19,12 +19,6 @@ // [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 com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.encryption.EncryptionConfigs; 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 59672690a5b..967929c02e8 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 @@ -47,7 +47,6 @@ 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.cloud.spanner.admin.instance.v1.InstanceAdminClient; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.io.BaseEncoding; 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 index 3ffe27f5152..c8c9e784107 100644 --- 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 @@ -39,7 +39,7 @@ */ @RunWith(JUnit4.class) @Ignore -public class EncryptionKeyIT extends SampleTestBase { +public class EncryptionKeyIT extends SampleTestBaseV2 { private static String key; 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 index 32066811b55..807dd5afbb4 100644 --- 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 @@ -21,10 +21,7 @@ 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; @@ -37,8 +34,12 @@ 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.BackupName; +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; @@ -57,7 +58,7 @@ /** 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"); @@ -68,24 +69,22 @@ 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); - com.example.spanner.SpannerSample.main(new String[] {command, instanceId, databaseId}); + SpannerSample.main(new String[] {command, instanceId, databaseId}); System.setOut(stdOut); return bout.toString(); } @@ -95,8 +94,7 @@ 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); key = @@ -134,20 +132,22 @@ private static void deleteStaleEncryptedTestInstances() throws InterruptedExcept } } - static void deleteStaleTestDatabases(String instanceId, String baseDbId) { + static void deleteStaleTestDatabases(String instanceId, String baseDbId) throws IOException { 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(); + try(DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) { + for (Database db : databaseAdminClient.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(); + } } } } @@ -155,297 +155,312 @@ 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.dropDatabase(DatabaseName.of(projectId, instanceId, encryptedDatabaseId)); + databaseAdminClient.dropDatabase(DatabaseName.of(projectId, instanceId, encryptedRestoreId)); + 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"); + runSample("write", databaseId); - out = runSample("delete"); + out = runSample("delete", databaseId); assertThat(out).contains("Records deleted."); - runSample("write"); + runSample("write", databaseId); - out = runSample("read"); + 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); + out = runSample("readstaledata", databaseId); assertThat(out).contains("1 1 NULL"); - runSample("writetransaction"); - out = runSample("querymarketingbudget"); + runSample("writetransaction", databaseId); + out = runSample("querymarketingbudget", databaseId); assertThat(out).contains("1 1 300000"); assertThat(out).contains("2 2 300000"); - runSample("addindex"); - out = runSample("queryindex"); + runSample("addindex", databaseId); + out = runSample("queryindex", databaseId); assertThat(out).contains("Go, Go, Go"); assertThat(out).contains("Forever Hold Your Peace"); assertThat(out).doesNotContain("Green"); - out = runSample("readindex"); + 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"); + runSample("addstoringindex", databaseId); + out = runSample("readstoringindex", databaseId); assertThat(out).contains("300000"); - out = runSample("readonlytransaction"); + 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); - 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"); + 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 backupId = 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"); - } + "%s_%02d", + dbId.getDatabase(), LocalDate.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR)); + 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) { + out = runSample("createbackup", databaseId); + assertThat(out).contains("Created backup [" + backupName.toString() + "]"); + + out = runSample("cancelcreatebackup", databaseId); + assertThat(out).contains( + "Backup operation for [" + backupId + "_cancel] successfully"); + + // TODO: remove try-catch when filtering on metadata fields works. try { - out = runSample("restorebackup"); + out = runSample("listbackupoperations", databaseId); assertThat(out).contains( - "Restored database [" - + dbId.getName() - + "] from [" - + backupId.getName() - + "]"); - restored = true; - break; + String.format( + "Backup %s on database %s pending:", + backupName.toString(), + 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"); + } + + out = runSample("listbackups", databaseId); + assertThat(out).contains("All backups:"); + assertThat(out).contains( + String.format("All backups with backup name containing \"%s\":", backupName.toString())); + 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, backupName.toString())).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", databaseId); + assertThat(out).contains( + "Restored database [" + + dbId.getName() + + "] 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) { + out = runSample("listdatabaseoperations", databaseId); + assertThat(out).contains( + String.format( + "Database %s restored from backup", + DatabaseId.of( + dbId.getInstanceId(), SpannerSample.createRestoredSampleDbId(dbId)) + .getName())); + } + + out = runSample("updatebackup", databaseId); assertThat(out).contains( - String.format( - "Database %s restored from backup", - DatabaseId.of( - dbId.getInstanceId(), - com.example.spanner.admin.generated.SpannerSample.createRestoredSampleDbId(dbId)) - .getName())); - } + String.format("Updated backup [" + backupId + "]")); - 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. + databaseAdminClient.dropDatabase(DatabaseName.of(projectId, + dbId.getInstanceId().getInstance(), SpannerSample.createRestoredSampleDbId(dbId))); - // 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", databaseId); + assertThat(out).contains("Deleted backup [" + backupId + "]"); - out = runSample("deletebackup"); - assertThat(out).contains("Deleted backup [" + backupId + "]"); + } catch (Exception ex) { + } } @Test @@ -463,14 +478,14 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { .get(); try { String out = SampleRunner - .runSample(() -> CreateDatabaseWithEncryptionKey.createDatabaseWithEncryptionKey(dbClient, + .runSample(() -> CreateDatabaseWithEncryptionKey.createDatabaseWithEncryptionKey(databaseAdminClient, 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, + () -> CreateBackupWithEncryptionKey.createBackupWithEncryptionKey(databaseAdminClient, projectId, instanceId, encryptedDatabaseId, encryptedBackupId, key), new ShouldRetryBackupOperation()); assertThat(out).containsMatch(String.format( @@ -479,7 +494,7 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { projectId, instanceId, encryptedBackupId, key)); out = SampleRunner.runSampleWithRetry( - () -> RestoreBackupWithEncryptionKey.restoreBackupWithEncryptionKey(dbClient, projectId, + () -> RestoreBackupWithEncryptionKey.restoreBackupWithEncryptionKey(databaseAdminClient, projectId, instanceId, encryptedBackupId, encryptedRestoreId, key), new ShouldRetryBackupOperation()); assertThat(out).contains(String.format( @@ -497,7 +512,8 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { } 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.getInstance()).iterateAll()) { int attempts = 0; while (attempts < 30) { try { @@ -530,11 +546,14 @@ 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(() -> { try { - com.example.spanner.admin.generated.CreateInstanceExample.createInstance( + CreateInstanceExample.createInstance( dbId.getInstanceId().getProject(), instanceId); } catch (IOException ex) { } finally { From 0182569008180c4e9adfc9c9240ce261147ae673 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Fri, 16 Feb 2024 00:59:16 +0530 Subject: [PATCH 10/30] fix: all compile issues. --- .../CreateBackupWithEncryptionKey.java | 61 +++++++++++-------- .../RestoreBackupWithEncryptionKey.java | 44 ++++++------- .../admin/generated/SpannerSample.java | 5 +- .../admin/generated/EncryptionKeyIT.java | 1 - .../admin/generated/SpannerSampleIT.java | 15 ++--- 5 files changed, 69 insertions(+), 57 deletions(-) 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 c6bd1606bef..fc1096e6aab 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,12 +18,19 @@ // [START spanner_create_backup_with_encryption_key] -import com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.Timestamp; 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; @@ -32,7 +39,7 @@ public class CreateBackupWithEncryptionKey { - static void createBackupWithEncryptionKey() throws InterruptedException { + static void createBackupWithEncryptionKey() throws IOException { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; String instanceId = "my-instance"; @@ -41,9 +48,7 @@ static void createBackupWithEncryptionKey() throws InterruptedException { String kmsKeyName = "projects/" + projectId + "/locations//keyRings//cryptoKeys/"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + try (DatabaseAdminClient adminClient = DatabaseAdminClient.create()) { createBackupWithEncryptionKey( adminClient, projectId, @@ -55,24 +60,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(backupName.toString()) + .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()); @@ -84,14 +94,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/admin/generated/RestoreBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/RestoreBackupWithEncryptionKey.java index 432194104b8..9e0c8e1e8ab 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 @@ -18,16 +18,22 @@ // [START spanner_restore_backup_with_encryption_key] -import com.google.api.gax.longrunning.OperationFuture; +import static com.google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION; + 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.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() { + static void restoreBackupWithEncryptionKey() throws IOException { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; String instanceId = "my-instance"; @@ -36,9 +42,7 @@ static void restoreBackupWithEncryptionKey() { String kmsKeyName = "projects/" + projectId + "/locations//keyRings//cryptoKeys/"; - try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + try (DatabaseAdminClient adminClient = DatabaseAdminClient.create()) { restoreBackupWithEncryptionKey( adminClient, projectId, @@ -51,19 +55,17 @@ 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(DatabaseName.of(projectId, instanceId, restoreId).toString()) + .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()); @@ -75,9 +77,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/admin/generated/SpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java index 967929c02e8..59f78278490 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 @@ -1574,8 +1574,9 @@ static void createBackup(DatabaseAdminClient dbAdminClient, String projectId, St 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.of(projectId, instanceId, backupId).toString()) + .setName(backupName.toString()) .setDatabase(DatabaseName.of(projectId, instanceId, databaseId).toString()) .setExpireTime(expireTime).setVersionTime(versionTime).build(); @@ -1584,7 +1585,7 @@ static void createBackup(DatabaseAdminClient dbAdminClient, String projectId, St try { // Wait for the backup operation to complete. backup = dbAdminClient.createBackupAsync( - InstanceName.of(projectId, instanceId), backup, backupId).get(); + InstanceName.of(projectId, instanceId), backup, backupName.toString()).get(); System.out.println("Created backup [" + backup.getName() + "]"); } catch (ExecutionException e) { throw (SpannerException) e.getCause(); 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 index c8c9e784107..7c7b3bc2d3d 100644 --- 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 @@ -19,7 +19,6 @@ 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; 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 index 807dd5afbb4..c9533044cf3 100644 --- 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 @@ -37,6 +37,7 @@ 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.DatabaseName; import com.google.spanner.admin.database.v1.InstanceName; @@ -137,16 +138,16 @@ static void deleteStaleTestDatabases(String instanceId, String baseDbId) throws Pattern samplePattern = getTestDbIdPattern(baseDbId); Pattern restoredPattern = getTestDbIdPattern("restored"); try(DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) { - for (Database db : databaseAdminClient.listDatabases(instanceId).iterateAll()) { + for (com.google.spanner.admin.database.v1.Database db : databaseAdminClient.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 (db.getName().length() >= DBID_LENGTH) { + if (samplePattern.matcher(toComparableId(baseDbId, db.getName())).matches()) { + databaseAdminClient.dropDatabase(db.getName()); } - if (restoredPattern.matcher(toComparableId("restored", db.getId().getDatabase())) + if (restoredPattern.matcher(toComparableId("restored", db.getName())) .matches()) { - db.drop(); + databaseAdminClient.dropDatabase(db.getName()); } } } @@ -518,7 +519,7 @@ private static void deleteAllBackups(String instanceId) throws InterruptedExcept while (attempts < 30) { try { attempts++; - backup.delete(); + databaseAdminClient.deleteBackup(backup.getName()); break; } catch (SpannerException e) { if (e.getErrorCode() == ErrorCode.FAILED_PRECONDITION && e.getMessage() From aa8facac2dcd5db56d9e7ef417c50f6e3832caa9 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Fri, 16 Feb 2024 18:32:23 +0530 Subject: [PATCH 11/30] chore: compile and test PgSpannerSample. --- .../admin/generated/PgSpannerSample.java | 211 ++++++++---------- .../admin/generated/PgSpannerSampleIT.java | 45 ++-- 2 files changed, 123 insertions(+), 133 deletions(-) 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 2c0c6a71125..3c467fabbc4 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 @@ -16,16 +16,12 @@ package com.example.spanner.admin.generated; -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; @@ -45,13 +41,17 @@ 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.common.io.BaseEncoding; import com.google.longrunning.Operation; import com.google.protobuf.InvalidProtocolBufferException; import com.google.spanner.admin.database.v1.CreateBackupMetadata; -import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import com.google.spanner.admin.database.v1.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.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.util.ArrayList; @@ -194,15 +194,19 @@ 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() + "]"); + createTableUsingDdl(dbAdminClient, DatabaseName.parse(db.toString())); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw (SpannerException) e.getCause(); @@ -302,15 +306,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. @@ -423,16 +424,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. @@ -463,17 +460,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. @@ -655,32 +649,28 @@ 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(); @@ -748,17 +738,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. @@ -800,24 +787,19 @@ 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(); @@ -993,27 +975,23 @@ 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(); @@ -1324,9 +1302,12 @@ static void run( InstanceAdminClient instanceAdminClient, String command, DatabaseId database) { + DatabaseName databaseName = DatabaseName.of(database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase()); switch (command) { case "createdatabase": - createPostgreSqlDatabase(dbAdminClient, database); + createPostgreSqlDatabase(dbAdminClient, database.getInstanceId().getProject(), + database.getInstanceId().getInstance(), database.getDatabase()); break; case "write": writeExampleData(dbClient); @@ -1341,7 +1322,7 @@ static void run( read(dbClient); break; case "addmarketingbudget": - addMarketingBudget(dbAdminClient, database); + addMarketingBudget(dbAdminClient, databaseName); break; case "update": update(dbClient); @@ -1353,13 +1334,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); @@ -1380,13 +1361,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); @@ -1395,7 +1376,7 @@ static void run( queryMarketingBudgetWithTimestamp(dbClient); break; case "createtablewithtimestamp": - createTableWithTimestamp(dbAdminClient, database); + createTableWithTimestamp(dbAdminClient, databaseName); break; case "writewithtimestamp": writeExampleDataWithTimestamp(dbClient); @@ -1425,7 +1406,7 @@ static void run( updateUsingBatchDml(dbClient); break; case "createtablewithdatatypes": - createTableWithDatatypes(dbAdminClient, database); + createTableWithDatatypes(dbAdminClient, databaseName); break; case "writedatatypesdata": writeDatatypesData(dbClient); @@ -1517,7 +1498,7 @@ static void printUsageAndExit() { System.exit(1); } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { if (args.length != 3) { printUsageAndExit(); } @@ -1540,7 +1521,7 @@ public static void main(String[] args) { } // [START spanner_init_client] DatabaseClient dbClient = spanner.getDatabaseClient(db); - DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + DatabaseAdminClient dbAdminClient = DatabaseAdminClient.create(); InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); // [END spanner_init_client] 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 index 1ebc838947d..8349c4ac904 100644 --- 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 @@ -19,12 +19,16 @@ 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.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; @@ -40,7 +44,7 @@ */ @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"); @@ -54,11 +58,11 @@ public class PgSpannerSampleIT { 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(); + dbClient = DatabaseAdminClient.create(); dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); // Delete stale test databases that have been created earlier by this test, but not deleted. deleteStaleTestDatabases(); @@ -68,17 +72,20 @@ 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()) { 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 (db.getName().length() >= DBID_LENGTH) { + if (samplePattern.matcher( + toComparableId( + DatabaseName.of(projectId, instanceId, PgSpannerSampleIT.baseDbId).toString(), + db.getName())).matches()) { + dbClient.dropDatabase(db.getName()); } - if (restoredPattern.matcher(toComparableId("restored", db.getId().getDatabase())) + if (restoredPattern.matcher(toComparableId( + DatabaseName.of(projectId, instanceId, "restored").toString(), db.getName())) .matches()) { - db.drop(); + dbClient.dropDatabase(db.getName()); } } } @@ -87,12 +94,14 @@ static void deleteStaleTestDatabases() { @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); + DatabaseName.of(projectId, dbId.getInstanceId().getInstance(), dbId.getDatabase())); + dbClient.dropDatabase( + DatabaseName.of(projectId, dbId.getInstanceId().getInstance(), + SpannerSample.createRestoredSampleDbId(dbId))); + dbClient.dropDatabase(DatabaseName.of(projectId, instanceId, encryptedDatabaseId)); + dbClient.dropDatabase(DatabaseName.of(projectId, instanceId, encryptedRestoreId)); + dbClient.deleteBackup(BackupName.of(projectId, instanceId, encryptedBackupId)); spanner.close(); } @@ -113,7 +122,7 @@ 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); From b806ef1c004a7356e03b51ef22ef300c39e48b7e Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Fri, 16 Feb 2024 19:44:51 +0530 Subject: [PATCH 12/30] fix: all issues with PgSpannerSample. --- .../spanner/admin/generated/PgSpannerSample.java | 14 +++++++++----- .../spanner/admin/generated/PgSpannerSampleIT.java | 10 ++++------ 2 files changed, 13 insertions(+), 11 deletions(-) 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 3c467fabbc4..3235aba8009 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 @@ -56,7 +56,6 @@ import java.math.BigDecimal; 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; @@ -206,7 +205,6 @@ static void createPostgreSqlDatabase( // Initiate the request which returns an OperationFuture. Database db = dbAdminClient.createDatabaseAsync(request).get(); System.out.println("Created database [" + db.getName() + "]"); - createTableUsingDdl(dbAdminClient, DatabaseName.parse(db.toString())); } catch (ExecutionException e) { // If the operation failed during execution, expose the cause. throw (SpannerException) e.getCause(); @@ -673,7 +671,7 @@ static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseName 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. @@ -1305,7 +1303,7 @@ static void run( DatabaseName databaseName = DatabaseName.of(database.getInstanceId().getProject(), database.getInstanceId().getInstance(), database.getDatabase()); switch (command) { - case "createdatabase": + case "createpgdatabase": createPostgreSqlDatabase(dbAdminClient, database.getInstanceId().getProject(), database.getInstanceId().getInstance(), database.getDatabase()); break; @@ -1505,6 +1503,7 @@ public static void main(String[] args) throws Exception { // [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]; @@ -1521,7 +1520,7 @@ public static void main(String[] args) throws Exception { } // [START spanner_init_client] DatabaseClient dbClient = spanner.getDatabaseClient(db); - DatabaseAdminClient dbAdminClient = DatabaseAdminClient.create(); + dbAdminClient = DatabaseAdminClient.create(); InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); // [END spanner_init_client] @@ -1529,6 +1528,11 @@ public static void main(String[] args) throws Exception { 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] 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 index 8349c4ac904..18dcbdb42a2 100644 --- 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 @@ -73,17 +73,15 @@ static void deleteStaleTestDatabases() { 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 (db.getName().length() >= DBID_LENGTH) { + if (databaseName.getDatabase().length() >= DBID_LENGTH) { if (samplePattern.matcher( - toComparableId( - DatabaseName.of(projectId, instanceId, PgSpannerSampleIT.baseDbId).toString(), - db.getName())).matches()) { + toComparableId(PgSpannerSampleIT.baseDbId, databaseName.getDatabase())).matches()) { dbClient.dropDatabase(db.getName()); } - if (restoredPattern.matcher(toComparableId( - DatabaseName.of(projectId, instanceId, "restored").toString(), db.getName())) + if (restoredPattern.matcher(toComparableId("restored", databaseName.getDatabase())) .matches()) { dbClient.dropDatabase(db.getName()); } From 7d5943d735620f7d0e8535595ea62ea27b60e4a4 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Sat, 17 Feb 2024 14:18:38 +0530 Subject: [PATCH 13/30] chore: compile and fix SpannerSample/SpannerSampleIT. --- .../admin/generated/SpannerSample.java | 8 ++++- .../admin/generated/SpannerSampleIT.java | 33 ++++++++++++++----- 2 files changed, 31 insertions(+), 10 deletions(-) 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 59f78278490..6dc06aea731 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 @@ -2247,6 +2247,7 @@ public static void main(String[] args) throws Exception { // [START init_client] SpannerOptions options = SpannerOptions.newBuilder().build(); Spanner spanner = options.getService(); + DatabaseAdminClient dbAdminClient = null; try { String command = args[0]; DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]); @@ -2271,7 +2272,7 @@ public static void main(String[] args) throws Exception { // [START init_client] DatabaseClient dbClient = spanner.getDatabaseClient(db); - DatabaseAdminClient dbAdminClient = DatabaseAdminClient.create(); + dbAdminClient = DatabaseAdminClient.create(); // Use client here... // [END init_client] @@ -2279,6 +2280,11 @@ public static void main(String[] args) throws Exception { 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/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java index c9533044cf3..1442185c366 100644 --- 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 @@ -21,7 +21,6 @@ import com.example.spanner.SampleRunner; import com.google.cloud.Timestamp; -import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.Instance; @@ -39,6 +38,7 @@ 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; @@ -97,7 +97,7 @@ public static void setUp() throws Exception { spanner = options.getService(); 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 +132,22 @@ private static void deleteStaleEncryptedTestInstances() throws InterruptedExcept } } } - - static void deleteStaleTestDatabases(String instanceId, String baseDbId) throws IOException { + static void deleteStaleTestDatabases() throws IOException { Timestamp now = Timestamp.now(); - Pattern samplePattern = getTestDbIdPattern(baseDbId); + Pattern samplePattern = getTestDbIdPattern(SpannerSampleIT.baseDbId); Pattern restoredPattern = getTestDbIdPattern("restored"); try(DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) { - for (com.google.spanner.admin.database.v1.Database db : databaseAdminClient.listDatabases(instanceId).iterateAll()) { + 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 (db.getName().length() >= DBID_LENGTH) { - if (samplePattern.matcher(toComparableId(baseDbId, db.getName())).matches()) { + if (databaseName.getDatabase().length() >= DBID_LENGTH) { + if (samplePattern.matcher( + toComparableId(SpannerSampleIT.baseDbId, databaseName.getDatabase())).matches()) { databaseAdminClient.dropDatabase(db.getName()); } - if (restoredPattern.matcher(toComparableId("restored", db.getName())) + if (restoredPattern.matcher(toComparableId("restored", databaseName.getDatabase())) .matches()) { databaseAdminClient.dropDatabase(db.getName()); } @@ -173,13 +175,16 @@ public void testSample() throws Exception { 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"); @@ -193,28 +198,38 @@ public void testSample() throws Exception { 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}"); From db5cbcaff40c14d1bbfd391741ffee7c79990720 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 19 Feb 2024 17:49:35 +0530 Subject: [PATCH 14/30] chore: fix kms configs and ITs. --- samples/install-without-bom/pom.xml | 4 ++-- samples/snapshot/pom.xml | 4 ++-- samples/snippets/pom.xml | 4 ++-- .../admin/generated/CreateBackupWithEncryptionKey.java | 2 +- .../com/example/spanner/admin/generated/SpannerSample.java | 4 ++-- .../com/example/spanner/admin/generated/SpannerSampleIT.java | 4 +++- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index 592328de2d9..85c0d2a6ac2 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 a17883b0acc..0f7e1626d5f 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 b6dd7ab693c..fe057b1037d 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/CreateBackupWithEncryptionKey.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/CreateBackupWithEncryptionKey.java index fc1096e6aab..39f070e639e 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 @@ -74,7 +74,7 @@ static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient, final CreateBackupRequest request = CreateBackupRequest.newBuilder() .setParent(InstanceName.of(projectId, instanceId).toString()) - .setBackupId(backupName.toString()) + .setBackupId(backupId) .setBackup(backup) .setEncryptionConfig( CreateBackupEncryptionConfig.newBuilder() 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 6dc06aea731..d8c816da884 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 @@ -1585,10 +1585,10 @@ static void createBackup(DatabaseAdminClient dbAdminClient, String projectId, St try { // Wait for the backup operation to complete. backup = dbAdminClient.createBackupAsync( - InstanceName.of(projectId, instanceId), backup, backupName.toString()).get(); + 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); } 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 index 1442185c366..aa12eb00a90 100644 --- 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 @@ -49,6 +49,7 @@ 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; @@ -476,6 +477,7 @@ public void testBackupSamples_withoutEncryption() { assertThat(out).contains("Deleted backup [" + backupId + "]"); } catch (Exception ex) { + Assert.fail("Exception raised => " + ex.getMessage()); } } @@ -529,7 +531,7 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { private static void deleteAllBackups(String instanceId) throws InterruptedException { InstanceName instanceName = InstanceName.of(projectId, instanceId); - for (Backup backup : databaseAdminClient.listBackups(instanceName.getInstance()).iterateAll()) { + for (Backup backup : databaseAdminClient.listBackups(instanceName.toString()).iterateAll()) { int attempts = 0; while (attempts < 30) { try { From 9240fbcaa7a50679b16b7c7c68155dc187da74b6 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 19 Feb 2024 19:40:55 +0530 Subject: [PATCH 15/30] chore: fix cancel backup tests. --- .../admin/generated/SpannerSample.java | 15 ++--- .../admin/generated/SpannerSampleIT.java | 55 +++++++++++++------ 2 files changed, 45 insertions(+), 25 deletions(-) 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 d8c816da884..55720c4e3cb 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 @@ -1632,14 +1632,14 @@ static void cancelCreateBackup( 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(); - // Try to cancel the backup operation. - System.out.println("Cancelling create backup operation for [" + backupId + "]..."); - pollingFuture.cancel(true); - // Wait for the operation to finish. // isDone will return true when the operation is complete, regardless of whether it was // successful or not. @@ -2262,11 +2262,8 @@ public static void main(String[] args) throws Exception { printUsageAndExit(); } // Generate a backup id for the sample database. - String backupId = - String.format( - "%s_%02d", - db.getDatabase(), LocalDate.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR)); - if (args.length == 4) { + String backupId = null; + if(args.length == 4) { backupId = args[3]; } 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 index aa12eb00a90..e0fbdfbae5b 100644 --- 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 @@ -86,7 +86,16 @@ private String runSample(String command, String databaseId) throws Exception { 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(); } @@ -375,27 +384,18 @@ public void testBackupSamples_withoutEncryption() { assertThat(out).contains("Created database"); assertThat(out).contains(dbId.getName()); - String backupId = - String.format( - "%s_%02d", - dbId.getDatabase(), LocalDate.now().get(ChronoField.ALIGNED_WEEK_OF_YEAR)); + String backupId = idGenerator.generateBackupId(); BackupName backupName = BackupName.of(projectId, instanceId, backupId); - out = runSample("createbackup", databaseId); + out = runSample("createbackup", databaseId, backupId); assertThat(out).contains("Created backup [" + backupName.toString() + "]"); - out = runSample("cancelcreatebackup", databaseId); - assertThat(out).contains( - "Backup operation for [" + backupId + "_cancel] successfully"); - // TODO: remove try-catch when filtering on metadata fields works. try { - out = runSample("listbackupoperations", databaseId); + out = runSample("listbackupoperations", databaseId, backupId); assertThat(out).contains( String.format( - "Backup %s on database %s pending:", - backupName.toString(), - dbId.getName())); + "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) { @@ -406,7 +406,7 @@ public void testBackupSamples_withoutEncryption() { out = runSample("listbackups", databaseId); assertThat(out).contains("All backups:"); assertThat(out).contains( - String.format("All backups with backup name containing \"%s\":", backupName.toString())); + String.format("All backups with backup name containing \"%s\":", backupName)); assertThat(out).contains(String.format( "All backups for databases with a name containing \"%s\":", dbId.getDatabase())); @@ -477,7 +477,30 @@ public void testBackupSamples_withoutEncryption() { assertThat(out).contains("Deleted backup [" + backupId + "]"); } catch (Exception ex) { - Assert.fail("Exception raised => " + ex.getMessage()); + 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()); } } From e752d32fab351659a63d16ab8d70abaf5b06cd94 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 19 Feb 2024 21:34:02 +0530 Subject: [PATCH 16/30] Update samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Knut Olav Løite --- .../java/com/example/spanner/admin/generated/SpannerSample.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 55720c4e3cb..eeb8e17ab4a 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 @@ -1669,7 +1669,7 @@ static void listBackupOperations( String projectId, String instanceId, String databaseId, String backupId) { InstanceName instanceName = InstanceName.of(projectId, instanceId); - // Get create backup operations for the sample database. + // Get 'CreateBackup' operations for the sample database. String filter = String.format( "(metadata.@type:type.googleapis.com/" From ca59fb60f883f11ca65ecc5aaa4b707fe7383db9 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Tue, 20 Feb 2024 11:00:07 +0530 Subject: [PATCH 17/30] chore: add log statements in tests. --- .../com/example/spanner/admin/generated/SpannerSampleIT.java | 5 +++++ 1 file changed, 5 insertions(+) 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 index e0fbdfbae5b..5b522eaa559 100644 --- 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 @@ -380,6 +380,7 @@ public void testBackupSamples_withoutEncryption() { 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()); @@ -387,11 +388,13 @@ public void testBackupSamples_withoutEncryption() { String backupId = idGenerator.generateBackupId(); 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( @@ -403,6 +406,7 @@ public void testBackupSamples_withoutEncryption() { assertThat(e.getMessage()).contains("Cannot evaluate filter expression"); } + System.out.println("List Backup ..."); out = runSample("listbackups", databaseId); assertThat(out).contains("All backups:"); assertThat(out).contains( @@ -426,6 +430,7 @@ public void testBackupSamples_withoutEncryption() { int restoreAttempts = 0; while (true) { try { + System.out.println("Restore Backup ..."); out = runSample("restorebackup", databaseId); assertThat(out).contains( "Restored database [" From fcf4457c07f77977e1d75fbdfab7305d8af0b6b4 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Tue, 20 Feb 2024 15:14:24 +0530 Subject: [PATCH 18/30] chore: fix lint errors and fix comments. --- .../admin/generated/CopyBackupSample.java | 36 ++-- .../admin/generated/PgSpannerSample.java | 167 ++++++++++-------- .../admin/generated/SpannerSample.java | 35 ++-- .../admin/generated/EncryptionKeyIT.java | 5 +- .../admin/generated/PgSpannerSampleIT.java | 3 +- .../admin/generated/SpannerSampleIT.java | 23 ++- 6 files changed, 140 insertions(+), 129 deletions(-) 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 2d2275b5999..9040785b190 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 @@ -18,7 +18,6 @@ // [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; @@ -27,9 +26,9 @@ 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.Instant; import java.time.OffsetDateTime; +import java.time.ZoneId; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -59,7 +58,6 @@ static void copyBackup( 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)) @@ -68,12 +66,11 @@ static void copyBackup( // 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 { + // Creates a copy of an existing backup. // Wait for the backup operation to complete. - destinationBackup = operation.get(); + destinationBackup = databaseAdminClient.copyBackup( + BackupId.of(projectId, instanceId, sourceBackupId), destinationBackup).get(); System.out.println("Copied backup [" + destinationBackup.getId() + "]"); } catch (ExecutionException e) { throw (SpannerException) e.getCause(); @@ -83,19 +80,16 @@ static void copyBackup( // 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; + String.format( + "Backup %s of size %d bytes was copied at %s for version of database at %s", + destinationBackup.getId().getName(), + destinationBackup.getSize(), + OffsetDateTime.ofInstant( + Instant.ofEpochSecond(destinationBackup.getProto().getCreateTime().getSeconds(), + destinationBackup.getProto().getCreateTime().getNanos()), ZoneId.systemDefault()), + OffsetDateTime.ofInstant( + Instant.ofEpochSecond(destinationBackup.getProto().getVersionTime().getSeconds(), + destinationBackup.getProto().getVersionTime().getNanos()), ZoneId.systemDefault()))); } } // [END spanner_copy_backup] 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 3235aba8009..9ec7ef47934 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 @@ -64,6 +64,7 @@ * Example code for using the Cloud Spanner PostgreSQL interface. */ public class PgSpannerSample { + // [START spanner_postgresql_insert_data] static final List SINGERS = Arrays.asList( @@ -81,7 +82,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; @@ -157,7 +160,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; @@ -273,9 +278,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), @@ -288,12 +293,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), @@ -402,11 +407,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", @@ -443,13 +448,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)); } @@ -484,13 +489,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", @@ -517,8 +522,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", @@ -533,10 +538,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", @@ -684,11 +689,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", @@ -762,14 +767,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", @@ -785,7 +790,8 @@ 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, + DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. dbAdminClient.updateDatabaseDdlAsync(databaseName, @@ -834,13 +840,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", @@ -973,7 +979,8 @@ 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, + DatabaseName databaseName) { try { // Initiate the request which returns an OperationFuture. dbAdminClient.updateDatabaseDdlAsync( @@ -1197,9 +1204,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)); @@ -1211,19 +1218,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)); @@ -1528,8 +1535,8 @@ public static void main(String[] args) throws Exception { run(dbClient, dbAdminClient, instanceAdminClient, command, db); // [START spanner_init_client] } finally { - if(dbAdminClient != null) { - if(!dbAdminClient.isShutdown() || !dbAdminClient.isTerminated()) { + if (dbAdminClient != null) { + if (!dbAdminClient.isShutdown() || !dbAdminClient.isTerminated()) { dbAdminClient.close(); } } @@ -1539,7 +1546,9 @@ public static void main(String[] args) throws Exception { System.out.println("Closed client"); } - /** Class to contain singer sample data. */ + /** + * Class to contain singer sample data. + */ static class Singer { final long singerId; @@ -1553,7 +1562,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/admin/generated/SpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/admin/generated/SpannerSample.java index eeb8e17ab4a..d80039c53f7 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 @@ -73,15 +73,12 @@ 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 @@ -1600,15 +1597,13 @@ static void createBackup(DatabaseAdminClient dbAdminClient, String projectId, St "Backup %s of size %d bytes was created at %s for version of database at %s", backup.getName(), backup.getSizeBytes(), - LocalDateTime.ofEpochSecond( - backup.getCreateTime().getSeconds(), - backup.getCreateTime().getNanos(), - OffsetDateTime.now().getOffset()), - LocalDateTime.ofEpochSecond( - backup.getVersionTime().getSeconds(), - backup.getVersionTime().getNanos(), - OffsetDateTime.now().getOffset()) - )); + 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] @@ -1731,8 +1726,7 @@ static void listDatabaseOperations( DatabaseAdminClient dbAdminClient, String projectId, String instanceId) { // Get optimize restored database operations. Timestamp last24Hours = Timestamp.newBuilder().setSeconds((TimeUnit.SECONDS.convert( - TimeUnit.HOURS.convert(new java.sql.Timestamp( - System.currentTimeMillis()).getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS.convert(Instant.now().getEpochSecond(), TimeUnit.SECONDS) - 24, TimeUnit.HOURS))).build(); String filter = String.format("(metadata.@type:type.googleapis.com/" + "google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata) AND " @@ -1914,8 +1908,9 @@ static void updateBackup(DatabaseAdminClient dbAdminClient, String projectId, System.out.println(String.format( "Updating expire time of backup [%s] to %s...", backupId.toString(), - LocalDateTime.ofEpochSecond( - newExpireTime.getSeconds(), newExpireTime.getNanos(), OffsetDateTime.now().getOffset()))); + java.time.OffsetDateTime.ofInstant( + Instant.ofEpochSecond(newExpireTime.getSeconds(), + newExpireTime.getNanos()), ZoneId.systemDefault()))); // Update expire time. backup = backup.toBuilder().setExpireTime(newExpireTime).build(); @@ -2263,7 +2258,7 @@ public static void main(String[] args) throws Exception { } // Generate a backup id for the sample database. String backupId = null; - if(args.length == 4) { + if (args.length == 4) { backupId = args[3]; } @@ -2277,8 +2272,8 @@ public static void main(String[] args) throws Exception { run(dbClient, dbAdminClient, command, db, backupId); // [START init_client] } finally { - if(dbAdminClient != null) { - if(!dbAdminClient.isShutdown() || !dbAdminClient.isTerminated()) { + if (dbAdminClient != null) { + if (!dbAdminClient.isShutdown() || !dbAdminClient.isTerminated()) { dbAdminClient.close(); } } 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 index 7c7b3bc2d3d..8cf1e6cec5d 100644 --- 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 @@ -33,8 +33,9 @@ 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 com.example.spanner.CreateDatabaseWithEncryptionKey}, + * {@link com.example.spanner.CreateBackupWithEncryptionKey} and + * {@link com.example.spanner.RestoreBackupWithEncryptionKey} */ @RunWith(JUnit4.class) @Ignore 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 index 18dcbdb42a2..71f995aa4b0 100644 --- 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 @@ -72,7 +72,8 @@ 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()) { + 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) { 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 index 5b522eaa559..5eb5bd981ca 100644 --- 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 @@ -57,10 +57,13 @@ 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 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"); @@ -86,16 +89,17 @@ private String runSample(String command, String databaseId) throws Exception { ByteArrayOutputStream bout = new ByteArrayOutputStream(); PrintStream out = new PrintStream(bout); System.setOut(out); - SpannerSample.main(new String[] {command, instanceId, databaseId, null}); + 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}); + SpannerSample.main(new String[]{command, instanceId, databaseId, backupId}); System.setOut(stdOut); return bout.toString(); } @@ -142,11 +146,12 @@ private static void deleteStaleEncryptedTestInstances() throws InterruptedExcept } } } + static void deleteStaleTestDatabases() throws IOException { Timestamp now = Timestamp.now(); Pattern samplePattern = getTestDbIdPattern(SpannerSampleIT.baseDbId); Pattern restoredPattern = getTestDbIdPattern("restored"); - try(DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) { + try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) { for (Database db : databaseAdminClient.listDatabases(InstanceName.of(projectId, instanceId)) .iterateAll()) { DatabaseName databaseName = DatabaseName.parse(db.getName()); @@ -524,14 +529,16 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { .get(); try { String out = SampleRunner - .runSample(() -> CreateDatabaseWithEncryptionKey.createDatabaseWithEncryptionKey(databaseAdminClient, + .runSample(() -> CreateDatabaseWithEncryptionKey.createDatabaseWithEncryptionKey( + databaseAdminClient, 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(databaseAdminClient, projectId, + () -> CreateBackupWithEncryptionKey.createBackupWithEncryptionKey(databaseAdminClient, + projectId, instanceId, encryptedDatabaseId, encryptedBackupId, key), new ShouldRetryBackupOperation()); assertThat(out).containsMatch(String.format( @@ -540,7 +547,8 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { projectId, instanceId, encryptedBackupId, key)); out = SampleRunner.runSampleWithRetry( - () -> RestoreBackupWithEncryptionKey.restoreBackupWithEncryptionKey(databaseAdminClient, projectId, + () -> RestoreBackupWithEncryptionKey.restoreBackupWithEncryptionKey(databaseAdminClient, + projectId, instanceId, encryptedBackupId, encryptedRestoreId, key), new ShouldRetryBackupOperation()); assertThat(out).contains(String.format( @@ -635,6 +643,7 @@ static String formatForTest(String name) { } static class ShouldRetryBackupOperation implements Predicate { + private static final int MAX_ATTEMPTS = 20; private int attempts = 0; From 89834e0ad946688f4333aa0d29a4edce9af65288 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Tue, 20 Feb 2024 15:21:52 +0530 Subject: [PATCH 19/30] chore: fix lint errors. --- .../admin/generated/CopyBackupSample.java | 51 ++++++++++--------- .../CreateDatabaseWithEncryptionKey.java | 2 +- .../admin/generated/SpannerSample.java | 6 +-- .../admin/generated/SpannerSampleIT.java | 1 + 4 files changed, 32 insertions(+), 28 deletions(-) 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 9040785b190..6fb6f5a688e 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 @@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit; public class CopyBackupSample { + static void copyBackup() { // TODO(developer): Replace these variables before running the sample. String projectId = "my-project"; @@ -40,29 +41,29 @@ static void copyBackup() { String sourceBackupId = "my-backup"; String destinationBackupId = "my-destination-backup"; try (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + 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) { + 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)); + Timestamp.ofTimeMicroseconds( + TimeUnit.MICROSECONDS.convert( + System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), + TimeUnit.MILLISECONDS)); Backup destinationBackup = - databaseAdminClient - .newBackupBuilder(BackupId.of(projectId, instanceId, destinationBackupId)) - .setExpireTime(expireTime) - .build(); + databaseAdminClient + .newBackupBuilder(BackupId.of(projectId, instanceId, destinationBackupId)) + .setExpireTime(expireTime) + .build(); // Initiate the request which returns an OperationFuture. System.out.println("Copying backup [" + destinationBackup.getId() + "]..."); @@ -80,16 +81,18 @@ static void copyBackup( // 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(), - OffsetDateTime.ofInstant( - Instant.ofEpochSecond(destinationBackup.getProto().getCreateTime().getSeconds(), - destinationBackup.getProto().getCreateTime().getNanos()), ZoneId.systemDefault()), - OffsetDateTime.ofInstant( - Instant.ofEpochSecond(destinationBackup.getProto().getVersionTime().getSeconds(), - destinationBackup.getProto().getVersionTime().getNanos()), ZoneId.systemDefault()))); + String.format( + "Backup %s of size %d bytes was copied at %s for version of database at %s", + destinationBackup.getId().getName(), + destinationBackup.getSize(), + OffsetDateTime.ofInstant( + Instant.ofEpochSecond(destinationBackup.getProto().getCreateTime().getSeconds(), + destinationBackup.getProto().getCreateTime().getNanos()), + ZoneId.systemDefault()), + OffsetDateTime.ofInstant( + Instant.ofEpochSecond(destinationBackup.getProto().getVersionTime().getSeconds(), + destinationBackup.getProto().getVersionTime().getNanos()), + ZoneId.systemDefault()))); } } // [END spanner_copy_backup] 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 72eb5476d7e..5010dcb9eb2 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 @@ -23,8 +23,8 @@ import com.google.common.collect.ImmutableList; import com.google.spanner.admin.database.v1.CreateDatabaseRequest; import com.google.spanner.admin.database.v1.EncryptionConfig; -import com.google.spanner.admin.database.v1.InstanceName; 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; import java.util.concurrent.TimeUnit; 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 d80039c53f7..7652af61eac 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 @@ -1700,8 +1700,8 @@ static void listBackupOperations( listBackupOperationsRequest = ListBackupOperationsRequest.newBuilder() .setParent(instanceName.toString()).setFilter(filter).build(); - ListBackupOperationsPagedResponse copyBackupOperations = databaseAdminClient.listBackupOperations( - listBackupOperationsRequest); + ListBackupOperationsPagedResponse copyBackupOperations = + databaseAdminClient.listBackupOperations(listBackupOperationsRequest); System.out.println("Copy Backup Operations:"); for (Operation op : copyBackupOperations.iterateAll()) { try { @@ -2244,7 +2244,7 @@ public static void main(String[] args) throws Exception { 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. 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 index 5eb5bd981ca..c1ea41ce0a9 100644 --- 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 @@ -610,6 +610,7 @@ public void testCreateInstanceSample() { CreateInstanceExample.createInstance( dbId.getInstanceId().getProject(), instanceId); } catch (IOException ex) { + System.out.println("Got exception => " + ex); } finally { spanner.getInstanceAdminClient().deleteInstance(instanceId); } From 478c343bdea800252a4ec22d533db1003b89bd36 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Tue, 20 Feb 2024 16:05:26 +0530 Subject: [PATCH 20/30] chore: fix lint errors. --- .../admin/generated/CreateDatabaseWithEncryptionKey.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5010dcb9eb2..2e9d9889f4c 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 @@ -22,8 +22,8 @@ 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.EncryptionConfig; 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; From d719f7fd4f27d8723788c68f0aaf38adf4a21a26 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Thu, 22 Feb 2024 12:04:55 +0530 Subject: [PATCH 21/30] chore: fix backup samples for restore use-case. --- .../RestoreBackupWithEncryptionKey.java | 2 +- .../admin/generated/EncryptionKeyIT.java | 16 +++++--------- .../admin/generated/SpannerSampleIT.java | 22 +++++++++---------- 3 files changed, 17 insertions(+), 23 deletions(-) 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 9e0c8e1e8ab..9c2ef5b3a75 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 @@ -58,7 +58,7 @@ static Void restoreBackupWithEncryptionKey(DatabaseAdminClient adminClient, RestoreDatabaseRequest request = RestoreDatabaseRequest.newBuilder() .setParent(InstanceName.of(projectId, instanceId).toString()) - .setDatabaseId(DatabaseName.of(projectId, instanceId, restoreId).toString()) + .setDatabaseId(restoreId) .setBackup(BackupName.of(projectId, instanceId, backupId).toString()) .setEncryptionConfig(RestoreDatabaseEncryptionConfig.newBuilder() .setEncryptionType(CUSTOMER_MANAGED_ENCRYPTION).setKmsKeyName(kmsKeyName)).build(); 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 index 8cf1e6cec5d..5b2513d91c8 100644 --- 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 @@ -24,6 +24,8 @@ 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; @@ -60,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/admin/generated/SpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java index c1ea41ce0a9..59702ad93c9 100644 --- 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 @@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; +import com.example.spanner.admin.generated.CreateDatabaseWithDefaultLeaderSample; import com.example.spanner.SampleRunner; import com.google.cloud.Timestamp; import com.google.cloud.spanner.DatabaseId; @@ -76,7 +77,6 @@ public class SpannerSampleIT extends SampleTestBaseV2 { Preconditions.checkNotNull(System.getProperty("spanner.test.key.name")); 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; @@ -175,7 +175,6 @@ static void deleteStaleTestDatabases() throws IOException { @AfterClass public static void tearDown() { databaseAdminClient.dropDatabase(DatabaseName.of(projectId, instanceId, encryptedDatabaseId)); - databaseAdminClient.dropDatabase(DatabaseName.of(projectId, instanceId, encryptedRestoreId)); databaseAdminClient.deleteBackup(BackupName.of(projectId, instanceId, encryptedBackupId)); spanner.close(); } @@ -517,9 +516,11 @@ public void testCancelBackupSamples() { @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)) @@ -529,17 +530,15 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { .get(); try { String out = SampleRunner - .runSample(() -> CreateDatabaseWithEncryptionKey.createDatabaseWithEncryptionKey( - databaseAdminClient, - 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(databaseAdminClient, projectId, - instanceId, encryptedDatabaseId, encryptedBackupId, key), + instanceId, databaseId, encryptedBackupId, key), new ShouldRetryBackupOperation()); assertThat(out).containsMatch(String.format( "Backup projects/%s/instances/%s/backups/%s of size \\d+ bytes " @@ -548,14 +547,13 @@ public void testEncryptedDatabaseAndBackupSamples() throws Exception { out = SampleRunner.runSampleWithRetry( () -> RestoreBackupWithEncryptionKey.restoreBackupWithEncryptionKey(databaseAdminClient, - projectId, - instanceId, encryptedBackupId, encryptedRestoreId, key), + 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 From d324ed6f2444f4f21a1719654f983289853f6d48 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Thu, 22 Feb 2024 19:45:47 +0530 Subject: [PATCH 22/30] fix: fix restore/list backup tests. --- .../admin/generated/SpannerSample.java | 60 +++++++------------ .../admin/generated/SpannerSampleIT.java | 23 +++---- 2 files changed, 32 insertions(+), 51 deletions(-) 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 7652af61eac..a338a7af369 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 @@ -54,7 +54,6 @@ import com.google.protobuf.FieldMask; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Timestamp; -import com.google.protobuf.util.Timestamps; import com.google.spanner.admin.database.v1.Backup; import com.google.spanner.admin.database.v1.BackupInfo; import com.google.spanner.admin.database.v1.BackupName; @@ -188,19 +187,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( @@ -1725,9 +1711,9 @@ static void listBackupOperations( static void listDatabaseOperations( DatabaseAdminClient dbAdminClient, String projectId, String instanceId) { // Get optimize restored database operations. - Timestamp last24Hours = Timestamp.newBuilder().setSeconds((TimeUnit.SECONDS.convert( - TimeUnit.HOURS.convert(Instant.now().getEpochSecond(), TimeUnit.SECONDS) - 24, - TimeUnit.HOURS))).build(); + 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); @@ -1787,14 +1773,13 @@ static void listBackups( } // List all backups that expire before a certain time. - Timestamp expireTime = - Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds(( - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(30)))).build(); + 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.toString())); + 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.toString())).build(); + .setFilter(String.format("expire_time < \"%s\"", expireTime)).build(); for (Backup backup : dbAdminClient.listBackups(listBackupsRequest).iterateAll()) { System.out.println(backup); @@ -1811,9 +1796,9 @@ static void listBackups( } // List all backups with a create time after a certain timestamp and that are also ready. - Timestamp createTime = - Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds(( - System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)))).build(); + 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())); @@ -1860,7 +1845,7 @@ static void restoreBackup( RestoreDatabaseRequest request = RestoreDatabaseRequest.newBuilder() .setParent(InstanceName.of(projectId, instanceId).toString()) - .setDatabaseId(DatabaseName.of(projectId, instanceId, restoreToDatabaseId).toString()) + .setDatabaseId(restoreToDatabaseId) .setBackup(backupName.toString()).build(); OperationFuture op = dbAdminClient.restoreDatabaseAsync(request); @@ -1872,7 +1857,7 @@ static void restoreBackup( System.out.println( "Restored database [" - + restoreInfo.getBackupInfo().getSourceDatabase() + + db.getName() + "] from [" + restoreInfo.getBackupInfo().getBackup() + "] with version time [" + backupInfo.getVersionTime() + "]"); @@ -1894,16 +1879,17 @@ static void updateBackup(DatabaseAdminClient dbAdminClient, String projectId, // 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(); - Timestamp newExpireTime = - Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds(( - TimeUnit.SECONDS.toMillis(currentExpireTime.getSeconds()) - + TimeUnit.NANOSECONDS.toMillis(currentExpireTime.getNanos()) - + TimeUnit.DAYS.toMillis(30)))).build(); + 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 = - Timestamps.compare(newExpireTime, backup.getMaxExpireTime()) < 0 ? newExpireTime - : backup.getMaxExpireTime(); + 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...", @@ -1913,7 +1899,7 @@ static void updateBackup(DatabaseAdminClient dbAdminClient, String projectId, newExpireTime.getNanos()), ZoneId.systemDefault()))); // Update expire time. - backup = backup.toBuilder().setExpireTime(newExpireTime).build(); + backup = backup.toBuilder().setExpireTime(newExpireTime.toProto()).build(); dbAdminClient.updateBackup(backup, FieldMask.newBuilder().addAllPaths(Lists.newArrayList("expire_time")).build()); System.out.println("Updated backup [" + backupId + "]"); @@ -2144,7 +2130,7 @@ static void run( case "restorebackup": restoreBackup( dbAdminClient, database.getInstanceId().getProject(), - database.getInstanceId().getInstance(), backupId, createRestoredSampleDbId(database)); + database.getInstanceId().getInstance(), backupId, database.getDatabase()); break; case "updatebackup": updateBackup(dbAdminClient, database.getInstanceId().getProject(), 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 index 59702ad93c9..e4f652d9749 100644 --- 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 @@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; -import com.example.spanner.admin.generated.CreateDatabaseWithDefaultLeaderSample; import com.example.spanner.SampleRunner; import com.google.cloud.Timestamp; import com.google.cloud.spanner.DatabaseId; @@ -55,8 +54,6 @@ 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} @@ -379,6 +376,7 @@ public void testSample() throws Exception { public void testBackupSamples_withoutEncryption() { String databaseId = idGenerator.generateDatabaseId(); DatabaseId dbId = DatabaseId.of(projectId, instanceId, databaseId); + String restoreDatabaseId = idGenerator.generateDatabaseId(); try { assertThat(instanceId).isNotNull(); @@ -414,7 +412,7 @@ public void testBackupSamples_withoutEncryption() { out = runSample("listbackups", databaseId); assertThat(out).contains("All backups:"); assertThat(out).contains( - String.format("All backups with backup name containing \"%s\":", backupName)); + 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())); @@ -435,10 +433,10 @@ public void testBackupSamples_withoutEncryption() { while (true) { try { System.out.println("Restore Backup ..."); - out = runSample("restorebackup", databaseId); + out = runSample("restorebackup", restoreDatabaseId, backupId); assertThat(out).contains( "Restored database [" - + dbId.getName() + + DatabaseName.of(projectId, instanceId, restoreDatabaseId).toString() + "] from [" + backupName + "]"); @@ -463,16 +461,14 @@ public void testBackupSamples_withoutEncryption() { } if (restored) { - out = runSample("listdatabaseoperations", databaseId); + out = runSample("listdatabaseoperations", restoreDatabaseId); assertThat(out).contains( String.format( "Database %s restored from backup", - DatabaseId.of( - dbId.getInstanceId(), SpannerSample.createRestoredSampleDbId(dbId)) - .getName())); + DatabaseId.of(dbId.getInstanceId(), restoreDatabaseId).getName())); } - out = runSample("updatebackup", databaseId); + out = runSample("updatebackup", databaseId, backupId); assertThat(out).contains( String.format("Updated backup [" + backupId + "]")); @@ -480,16 +476,15 @@ public void testBackupSamples_withoutEncryption() { // 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(), SpannerSample.createRestoredSampleDbId(dbId))); + dbId.getInstanceId().getInstance(), restoreDatabaseId)); - out = runSample("deletebackup", databaseId); + 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(); From 837034aeac024bf840997dd375d7cd6f88ae2325 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Thu, 22 Feb 2024 22:35:41 +0530 Subject: [PATCH 23/30] chore: fix PgSpannerSample sample and test. --- .../admin/generated/PgSpannerSample.java | 103 +++++++++++++----- .../admin/generated/PgSpannerSampleIT.java | 32 +----- .../admin/generated/SpannerSampleIT.java | 8 +- 3 files changed, 80 insertions(+), 63 deletions(-) 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 9ec7ef47934..51ee02ba6d5 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 @@ -22,9 +22,6 @@ import com.google.cloud.Timestamp; 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; @@ -42,14 +39,20 @@ 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; @@ -1240,22 +1243,25 @@ 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 (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( @@ -1269,23 +1275,53 @@ 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, + 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); @@ -1304,9 +1340,9 @@ 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) { @@ -1444,10 +1480,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,14 +1563,19 @@ public static void main(String[] args) throws Exception { + 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(); - InstanceAdminClient instanceAdminClient = spanner.getInstanceAdminClient(); // [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) { 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 index 71f995aa4b0..e4e3cf0d9db 100644 --- 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 @@ -23,17 +23,14 @@ 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.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.regex.Pattern; -import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,10 +46,6 @@ public class PgSpannerSampleIT extends SampleTestBaseV2 { // 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; @@ -63,7 +56,7 @@ public static void setUp() throws IOException { SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build(); spanner = options.getService(); dbClient = DatabaseAdminClient.create(); - dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); + dbId = DatabaseId.of(options.getProjectId(), instanceId, idGenerator.generateDatabaseId()); // Delete stale test databases that have been created earlier by this test, but not deleted. deleteStaleTestDatabases(); } @@ -91,19 +84,6 @@ static void deleteStaleTestDatabases() { } } - @AfterClass - public static void tearDown() { - dbClient.dropDatabase( - DatabaseName.of(projectId, dbId.getInstanceId().getInstance(), dbId.getDatabase())); - dbClient.dropDatabase( - DatabaseName.of(projectId, dbId.getInstanceId().getInstance(), - SpannerSample.createRestoredSampleDbId(dbId))); - dbClient.dropDatabase(DatabaseName.of(projectId, instanceId, encryptedDatabaseId)); - dbClient.dropDatabase(DatabaseName.of(projectId, instanceId, encryptedRestoreId)); - dbClient.deleteBackup(BackupName.of(projectId, 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(); @@ -117,17 +97,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) 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(); } @@ -135,7 +111,7 @@ private String runSample(String command) throws Exception { @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/admin/generated/SpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java index e4f652d9749..117bfa23cee 100644 --- 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 @@ -72,7 +72,6 @@ public class SpannerSampleIT extends SampleTestBaseV2 { 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 encryptedDatabaseId = formatForTest(baseDbId); private static final String encryptedBackupId = formatForTest(baseDbId); private static final long STALE_INSTANCE_THRESHOLD_SECS = TimeUnit.SECONDS.convert(24L, TimeUnit.HOURS); @@ -171,7 +170,6 @@ static void deleteStaleTestDatabases() throws IOException { @AfterClass public static void tearDown() { - databaseAdminClient.dropDatabase(DatabaseName.of(projectId, instanceId, encryptedDatabaseId)); databaseAdminClient.deleteBackup(BackupName.of(projectId, instanceId, encryptedBackupId)); spanner.close(); } @@ -409,7 +407,7 @@ public void testBackupSamples_withoutEncryption() { } System.out.println("List Backup ..."); - out = runSample("listbackups", databaseId); + out = runSample("listbackups", databaseId, backupId); assertThat(out).contains("All backups:"); assertThat(out).contains( String.format("All backups with backup name containing \"%s\":", backupId)); @@ -422,8 +420,8 @@ public void testBackupSamples_withoutEncryption() { 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, backupName.toString())).isEqualTo(7); + // 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 From f7623ed4fe69b42aab510afc5dbb3fb7ee80822a Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Fri, 23 Feb 2024 10:25:52 +0530 Subject: [PATCH 24/30] chore: fix delete backup test. --- .../admin/generated/PgSpannerSample.java | 11 ++++--- .../admin/generated/SpannerSample.java | 31 ++++++++++--------- .../admin/generated/PgSpannerSampleIT.java | 1 + .../admin/generated/SpannerSampleIT.java | 4 +++ 4 files changed, 29 insertions(+), 18 deletions(-) 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 51ee02ba6d5..b9853049d81 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 @@ -1247,7 +1247,8 @@ 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); + 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( @@ -1309,9 +1310,11 @@ static void listBackupOperations( 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); + 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); 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 a338a7af369..8a24f2ceb54 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 @@ -1711,9 +1711,11 @@ static void listBackupOperations( 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); + 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); @@ -1773,8 +1775,9 @@ static void listBackups( } // 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)); + 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 = @@ -1796,8 +1799,9 @@ static void listBackups( } // 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)); + 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( @@ -1885,11 +1889,10 @@ static void updateBackup(DatabaseAdminClient dbAdminClient, String projectId, + 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()); - + 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...", @@ -1919,12 +1922,12 @@ static void deleteBackup(DatabaseAdminClient dbAdminClient, dbAdminClient.getBackup(backupName); } catch (SpannerException e) { if (e.getErrorCode() == ErrorCode.NOT_FOUND) { + System.out.println("Deleted backup [" + backupId + "]"); + } else { System.out.println("Delete backup [" + backupId + "] failed"); - throw new RuntimeException("Delete backup [" + backupId + "] failed"); + throw new RuntimeException("Delete backup [" + backupId + "] failed", e); } - throw e; } - System.out.println("Deleted backup [" + backupId + "]"); } // [END spanner_delete_backup] 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 index e4e3cf0d9db..90885b71ce8 100644 --- 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 @@ -42,6 +42,7 @@ @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"); 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 index 117bfa23cee..116fee8eddc 100644 --- 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 @@ -459,6 +459,7 @@ public void testBackupSamples_withoutEncryption() { } if (restored) { + System.out.println("List Database Operations ..."); out = runSample("listdatabaseoperations", restoreDatabaseId); assertThat(out).contains( String.format( @@ -466,6 +467,7 @@ public void testBackupSamples_withoutEncryption() { DatabaseId.of(dbId.getInstanceId(), restoreDatabaseId).getName())); } + System.out.println("Updating backup ..."); out = runSample("updatebackup", databaseId, backupId); assertThat(out).contains( String.format("Updated backup [" + backupId + "]")); @@ -476,6 +478,7 @@ public void testBackupSamples_withoutEncryption() { 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 + "]"); @@ -483,6 +486,7 @@ public void testBackupSamples_withoutEncryption() { Assert.fail("Exception raised => " + ex.getCause()); } } + @Test public void testCancelBackupSamples() { String databaseId = idGenerator.generateDatabaseId(); From 3f90c1a332a163126f9c820ebd8b8c5538d55649 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Fri, 23 Feb 2024 10:31:04 +0530 Subject: [PATCH 25/30] chore: fix lint errors. --- .../example/spanner/admin/generated/PgSpannerSample.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 b9853049d81..b2e6eafb0f5 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 @@ -1247,8 +1247,8 @@ 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); + 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( @@ -1554,7 +1554,7 @@ public static void main(String[] args) throws Exception { 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. From bb7a6ccee1783e47bcef027b2f5ddcda11c3dfb2 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 23 Feb 2024 05:28:30 +0000 Subject: [PATCH 26/30] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20?= =?UTF-8?q?post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 56d668b0a65..4807860eed9 100644 --- a/README.md +++ b/README.md @@ -50,20 +50,20 @@ If you are using Maven without the BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.31.0') +implementation platform('com.google.cloud:libraries-bom:26.33.0') 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.58.0' +implementation 'com.google.cloud:google-cloud-spanner:6.60.0' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.58.0" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.60.0" ``` @@ -331,7 +331,10 @@ 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) | +| 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 Default Leader Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/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/generated/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/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 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) | @@ -340,6 +343,9 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | Get Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/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 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 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 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) | @@ -444,7 +450,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.58.0 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-spanner/6.60.0 [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 From 01058bc1041e65fb72f19bd92a3e79b7c4d2d298 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 23 Feb 2024 08:05:29 +0000 Subject: [PATCH 27/30] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20?= =?UTF-8?q?post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a0b1963e7af..5c1f4f58f74 100644 --- a/README.md +++ b/README.md @@ -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" ``` @@ -509,16 +509,15 @@ 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) | -| 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 Default Leader Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/admin/generated/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/generated/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/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) | | 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) | @@ -536,14 +535,14 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | 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 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) | | 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) | @@ -650,7 +649,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 From 0dcce23c944609e847b8454ccdfceca17ac8c1e6 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Fri, 23 Feb 2024 16:02:28 +0530 Subject: [PATCH 28/30] chore: add IT for CopyBackupSample. --- .../admin/generated/CopyBackupSample.java | 46 ++++---- .../admin/generated/SpannerSample.java | 6 +- .../spanner/admin/generated/CopyBackupIT.java | 110 ++++++++++++++++++ .../admin/generated/SpannerSampleIT.java | 2 +- 4 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 samples/snippets/src/test/java/com/example/spanner/admin/generated/CopyBackupIT.java 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 6fb6f5a688e..ebac469ca7a 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,13 @@ // [START spanner_copy_backup] 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.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,15 +34,14 @@ public class CopyBackupSample { - static void copyBackup() { + 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 (Spanner spanner = - SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { - DatabaseAdminClient databaseAdminClient = spanner.getDatabaseAdminClient(); + + try (DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.create()) { copyBackup(databaseAdminClient, projectId, instanceId, sourceBackupId, destinationBackupId); } } @@ -59,39 +58,36 @@ static void copyBackup( TimeUnit.MICROSECONDS.convert( System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS)); - 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() + "]..."); + 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.copyBackup( - BackupId.of(projectId, instanceId, sourceBackupId), destinationBackup).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(), + destinationBackup.getName(), + destinationBackup.getSizeBytes(), OffsetDateTime.ofInstant( - Instant.ofEpochSecond(destinationBackup.getProto().getCreateTime().getSeconds(), - destinationBackup.getProto().getCreateTime().getNanos()), + Instant.ofEpochSecond(destinationBackup.getCreateTime().getSeconds(), + destinationBackup.getCreateTime().getNanos()), ZoneId.systemDefault()), OffsetDateTime.ofInstant( - Instant.ofEpochSecond(destinationBackup.getProto().getVersionTime().getSeconds(), - destinationBackup.getProto().getVersionTime().getNanos()), + Instant.ofEpochSecond(destinationBackup.getVersionTime().getSeconds(), + destinationBackup.getVersionTime().getNanos()), ZoneId.systemDefault()))); } } 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 8a24f2ceb54..d05b79fafc2 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 @@ -70,6 +70,8 @@ import com.google.spanner.admin.database.v1.RestoreDatabaseRequest; import com.google.spanner.admin.database.v1.RestoreInfo; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; import java.math.BigDecimal; import java.time.Instant; import java.time.ZoneId; @@ -1920,8 +1922,8 @@ static void deleteBackup(DatabaseAdminClient dbAdminClient, // Verify that the backup is deleted. try { dbAdminClient.getBackup(backupName); - } catch (SpannerException e) { - if (e.getErrorCode() == ErrorCode.NOT_FOUND) { + } catch (StatusRuntimeException e) { + if (e.getStatus().getCode() == Status.Code.NOT_FOUND) { System.out.println("Deleted backup [" + backupId + "]"); } else { System.out.println("Delete backup [" + backupId + "] failed"); 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 00000000000..5b4b340bb9a --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/admin/generated/CopyBackupIT.java @@ -0,0 +1,110 @@ +/* + * 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).contains("Created backup [" + + BackupName.of(projectId, instanceId, sourceBackupId) + "]"); + + 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/SpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/admin/generated/SpannerSampleIT.java index 116fee8eddc..a3636ab5cfe 100644 --- 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 @@ -375,6 +375,7 @@ 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(); @@ -385,7 +386,6 @@ public void testBackupSamples_withoutEncryption() { assertThat(out).contains("Created database"); assertThat(out).contains(dbId.getName()); - String backupId = idGenerator.generateBackupId(); BackupName backupName = BackupName.of(projectId, instanceId, backupId); System.out.println("Creating Backup ..."); From fa88a450032551a6c5985ef6365d865dd1378d80 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Fri, 23 Feb 2024 16:17:51 +0530 Subject: [PATCH 29/30] chore: fix lint errors. --- .../com/example/spanner/admin/generated/CopyBackupIT.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 index 5b4b340bb9a..1d510428880 100644 --- 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 @@ -67,8 +67,10 @@ public void testEncryptedDatabaseAndBackupAndRestore() throws Exception { CreateBackupWithEncryptionKey.createBackupWithEncryptionKey( databaseAdminClient, projectId, instanceId, databaseId, sourceBackupId, key ), new ShouldRetryBackupOperation()); - assertThat(out).contains("Created backup [" + - BackupName.of(projectId, instanceId, sourceBackupId) + "]"); + 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( From e3630bcfeef0336aa0b5e5e97878ad4b5145c3f8 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Fri, 23 Feb 2024 18:07:49 +0530 Subject: [PATCH 30/30] chore: refactor test for delete backup. --- .../admin/generated/SpannerSample.java | 9 +++--- .../admin/generated/SpannerSampleIT.java | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) 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 d05b79fafc2..6e805dc1322 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 @@ -21,12 +21,13 @@ 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.ErrorCode; import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; @@ -70,8 +71,6 @@ import com.google.spanner.admin.database.v1.RestoreDatabaseRequest; import com.google.spanner.admin.database.v1.RestoreInfo; import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; import java.math.BigDecimal; import java.time.Instant; import java.time.ZoneId; @@ -1922,8 +1921,8 @@ static void deleteBackup(DatabaseAdminClient dbAdminClient, // Verify that the backup is deleted. try { dbAdminClient.getBackup(backupName); - } catch (StatusRuntimeException e) { - if (e.getStatus().getCode() == Status.Code.NOT_FOUND) { + } catch (NotFoundException e) { + if (e.getStatusCode().getCode() == Code.NOT_FOUND) { System.out.println("Deleted backup [" + backupId + "]"); } else { System.out.println("Delete backup [" + backupId + "] failed"); 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 index a3636ab5cfe..cf719ff9e43 100644 --- 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 @@ -560,6 +560,35 @@ 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 { InstanceName instanceName = InstanceName.of(projectId, instanceId); for (Backup backup : databaseAdminClient.listBackups(instanceName.toString()).iterateAll()) {