From 376e4491540e02f99d72b25a3062f563ad758b57 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Fri, 25 Mar 2022 10:26:12 +0530 Subject: [PATCH 01/13] Added samples for PG dialect databases --- .../com/example/spanner/PgSpannerSample.java | 643 ++++++++++++++++++ .../example/spanner/PgSpannerSampleIT.java | 176 +++++ 2 files changed, 819 insertions(+) create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java new file mode 100644 index 00000000000..1ef8eea294d --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -0,0 +1,643 @@ +package com.example.spanner; + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.*; +import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class PgSpannerSample { + // [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")); + + // [START spanner_create_database] + static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = dbAdminClient.createDatabase( + dbAdminClient.newDatabaseBuilder(id).setDialect(Dialect.POSTGRESQL).build(), + Collections.emptyList()); + try { + // Initiate the request which returns an OperationFuture. + Database db = op.get(); + System.out.println("Created database [" + db.getId() + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_create_database] + + // [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_datatypes_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_create_database] + + // [START spanner_add_column] + static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget 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_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 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_query_data_with_new_column] + + // [START spanner_create_index] + static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), + null); + 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_create_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, 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_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), + // TODO: MarketingBudget uppercase not supported, keeping lowercase for now + resultSet.isNull("marketingbudget") ? "NULL" : resultSet.getLong(2)); + } + } + } + // [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_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_query_singers_table] + + + // [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 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_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 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_dml_getting_started_update] + + // [START spanner_create_table_using_ddl] + static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL," + + " FirstName character varying(1024)," + + " LastName character varying(1024)," + + " SingerInfo bytea," + + " PRIMARY KEY (SingerId)" + + ")", + "CREATE TABLE Albums (" + + " SingerId bigint NOT NULL," + + " AlbumId bigint NOT NULL," + + " AlbumTitle character varying(1024)," + + " PRIMARY KEY (SingerId, AlbumId)" + + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Created Singers & Albums tables in database: [" + id + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_create_table_using_ddl] + + static void run( + DatabaseClient dbClient, + DatabaseAdminClient dbAdminClient, + String command, + DatabaseId database) { + switch (command) { + case "createdatabase": + createDatabase(dbAdminClient, database); + break; + case "write": + writeExampleData(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; + 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.exit(1); + } + + public static void main(String[] args) { + if (args.length != 3) { + 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]); + + // 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(); + } + + DatabaseClient dbClient = spanner.getDatabaseClient(db); + DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + + // Use client here... + run(dbClient, dbAdminClient, command, db); + } finally { + spanner.close(); + } + // [END 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/test/java/com/example/spanner/PgSpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java new file mode 100644 index 00000000000..9e586cf79bc --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java @@ -0,0 +1,176 @@ +package com.example.spanner; + +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +import static com.google.common.truth.Truth.assertThat; + +@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) { + PrintStream stdOut = System.out; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + 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("createdatabase"); + 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}"); + } +} From db81ea69c396f4a163c5e27355a00a5944347c91 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Fri, 25 Mar 2022 10:26:12 +0530 Subject: [PATCH 02/13] docs: add samples for postgresql --- .../com/example/spanner/PgSpannerSample.java | 643 ++++++++++++++++++ .../example/spanner/PgSpannerSampleIT.java | 176 +++++ 2 files changed, 819 insertions(+) create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java new file mode 100644 index 00000000000..175f1004af4 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -0,0 +1,643 @@ +package com.example.spanner; + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.*; +import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class PgSpannerSample { + // [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")); + + // [START spanner_create_database] + static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = dbAdminClient.createDatabase( + dbAdminClient.newDatabaseBuilder(id).setDialect(Dialect.POSTGRESQL).build(), + Collections.emptyList()); + try { + // Initiate the request which returns an OperationFuture. + Database db = op.get(); + System.out.println("Created database [" + db.getId() + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_create_database] + + // [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_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, 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_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 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_query_data_with_new_column] + + // [START spanner_create_index] + static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { + OperationFuture op = + adminClient.updateDatabaseDdl( + dbId.getInstanceId().getInstance(), + dbId.getDatabase(), + Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), + null); + 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_create_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, 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_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), + // TODO: MarketingBudget uppercase not supported, keeping lowercase for now + resultSet.isNull("marketingbudget") ? "NULL" : resultSet.getLong(2)); + } + } + } + // [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_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_query_singers_table] + + + // [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 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_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 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_dml_getting_started_update] + + // [START spanner_create_table_using_ddl] + static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL," + + " FirstName character varying(1024)," + + " LastName character varying(1024)," + + " SingerInfo bytea," + + " PRIMARY KEY (SingerId)" + + ")", + "CREATE TABLE Albums (" + + " SingerId bigint NOT NULL," + + " AlbumId bigint NOT NULL," + + " AlbumTitle character varying(1024)," + + " PRIMARY KEY (SingerId, AlbumId)" + + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), + null); + try { + // Initiate the request which returns an OperationFuture. + op.get(); + System.out.println("Created Singers & Albums tables in database: [" + id + "]"); + } catch (ExecutionException e) { + // If the operation failed during execution, expose the cause. + throw (SpannerException) e.getCause(); + } catch (InterruptedException e) { + // Throw when a thread is waiting, sleeping, or otherwise occupied, + // and the thread is interrupted, either before or during the activity. + throw SpannerExceptionFactory.propagateInterrupt(e); + } + } + // [END spanner_create_table_using_ddl] + + static void run( + DatabaseClient dbClient, + DatabaseAdminClient dbAdminClient, + String command, + DatabaseId database) { + switch (command) { + case "createdatabase": + createDatabase(dbAdminClient, database); + break; + case "write": + writeExampleData(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; + 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.exit(1); + } + + public static void main(String[] args) { + if (args.length != 3) { + 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]); + + // 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(); + } + + DatabaseClient dbClient = spanner.getDatabaseClient(db); + DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + + // Use client here... + run(dbClient, dbAdminClient, command, db); + } finally { + spanner.close(); + } + // [END 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/test/java/com/example/spanner/PgSpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java new file mode 100644 index 00000000000..9e586cf79bc --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java @@ -0,0 +1,176 @@ +package com.example.spanner; + +import com.google.cloud.Timestamp; +import com.google.cloud.spanner.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +import static com.google.common.truth.Truth.assertThat; + +@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) { + PrintStream stdOut = System.out; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + 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("createdatabase"); + 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}"); + } +} From e764e9d784dfe63666111a84b0e8d191c588a003 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Sat, 26 Mar 2022 07:11:17 +0530 Subject: [PATCH 03/13] docs: samples tag fixes --- .../src/main/java/com/example/spanner/PgSpannerSample.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java index 175f1004af4..e11244e395d 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -12,7 +12,6 @@ import java.util.concurrent.ExecutionException; public class PgSpannerSample { - // [START spanner_insert_data] static final List SINGERS = Arrays.asList( new Singer(1, "Marc", "Richards"), @@ -588,6 +587,7 @@ public static void main(String[] args) { SpannerOptions options = SpannerOptions.newBuilder().build(); Spanner spanner = options.getService(); try { + // [END init_client] String command = args[0]; DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]); @@ -600,11 +600,11 @@ public static void main(String[] args) { + clientProject); printUsageAndExit(); } - + // [START init_client] DatabaseClient dbClient = spanner.getDatabaseClient(db); DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - // Use client here... + // [END init_client] run(dbClient, dbAdminClient, command, db); } finally { spanner.close(); From 332f15d1cf510c324e0a51d247a27cd43cf4e805 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Sat, 26 Mar 2022 07:15:10 +0530 Subject: [PATCH 04/13] docs: samples tag fixes --- .../main/java/com/example/spanner/PgSpannerSample.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java index e11244e395d..0e0e32a17b3 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -583,11 +583,10 @@ public static void main(String[] args) { if (args.length != 3) { printUsageAndExit(); } - // [START init_client] + // [START spanner_init_client] SpannerOptions options = SpannerOptions.newBuilder().build(); Spanner spanner = options.getService(); try { - // [END init_client] String command = args[0]; DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]); @@ -600,16 +599,17 @@ public static void main(String[] args) { + clientProject); printUsageAndExit(); } - // [START init_client] + // [START spanner_init_client] DatabaseClient dbClient = spanner.getDatabaseClient(db); DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); + // [END spanner_init_client] + // Use client here... - // [END init_client] run(dbClient, dbAdminClient, command, db); } finally { spanner.close(); } - // [END init_client] + // [END spanner_init_client] System.out.println("Closed client"); } From 45b1358dc7357e203a000e4f810dc245e8d33a98 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Sat, 26 Mar 2022 07:17:23 +0530 Subject: [PATCH 05/13] docs: samples tag violation fixes --- .../src/main/java/com/example/spanner/PgSpannerSample.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java index 0e0e32a17b3..3b0e21c3a2c 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -587,6 +587,7 @@ public static void main(String[] args) { 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]); @@ -606,6 +607,7 @@ public static void main(String[] args) { // Use client here... run(dbClient, dbAdminClient, command, db); + // [START spanner_init_client] } finally { spanner.close(); } From 9d6b60e86ab5e5ff66a37e7dcc94b18ceb1a9d32 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Mon, 28 Mar 2022 08:53:13 +0530 Subject: [PATCH 06/13] docs: samples imports and indent fixes --- .../com/example/spanner/PgSpannerSample.java | 64 +++++++++++++++---- .../example/spanner/PgSpannerSampleIT.java | 27 ++++---- 2 files changed, 69 insertions(+), 22 deletions(-) diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java index 3b0e21c3a2c..16a1b172d73 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -1,7 +1,38 @@ +/* + * 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 com.google.api.gax.longrunning.OperationFuture; -import com.google.cloud.spanner.*; +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.Key; +import com.google.cloud.spanner.KeySet; +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.ReadOnlyTransaction; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.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.spanner.admin.database.v1.CreateDatabaseMetadata; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; @@ -84,7 +115,8 @@ static void query(DatabaseClient dbClient) { .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)); + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), + resultSet.getString(2)); } } } @@ -101,7 +133,8 @@ static void read(DatabaseClient dbClient) { Arrays.asList("SingerId", "AlbumId", "AlbumTitle"))) { while (resultSet.next()) { System.out.printf( - "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2)); + "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), + resultSet.getString(2)); } } } @@ -211,7 +244,9 @@ static void queryMarketingBudget(DatabaseClient dbClient) { try (ResultSet resultSet = dbClient .singleUse() - .executeQuery(Statement.of("SELECT singerid as \"SingerId\", albumid as \"AlbumId\", marketingbudget as \"MarketingBudget\" FROM Albums"))) { + .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", @@ -219,7 +254,8 @@ static void queryMarketingBudget(DatabaseClient dbClient) { 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("MarketingBudget") ? "NULL" : + resultSet.getLong("MarketingBudget")); } } } @@ -325,7 +361,8 @@ static void readOnlyTransaction(DatabaseClient dbClient) { while (queryResultSet.next()) { System.out.printf( "%d %d %s\n", - queryResultSet.getLong(0), queryResultSet.getLong(1), queryResultSet.getString(2)); + queryResultSet.getLong(0), queryResultSet.getLong(1), + queryResultSet.getString(2)); } try (ResultSet readResultSet = transaction.read( @@ -333,7 +370,8 @@ static void readOnlyTransaction(DatabaseClient dbClient) { while (readResultSet.next()) { System.out.printf( "%d %d %s\n", - readResultSet.getLong(0), readResultSet.getLong(1), readResultSet.getString(2)); + readResultSet.getLong(0), readResultSet.getLong(1), + readResultSet.getString(2)); } } } @@ -345,7 +383,8 @@ 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"))) { + .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", @@ -381,7 +420,8 @@ static void writeUsingDml(DatabaseClient dbClient) { static void queryWithParameter(DatabaseClient dbClient) { Statement statement = Statement.newBuilder( - "SELECT singerid AS \"SingerId\", firstname as \"FirstName\", lastname as \"LastName\" " + "SELECT singerid AS \"SingerId\", " + + "firstname as \"FirstName\", lastname as \"LastName\" " + "FROM Singers " + "WHERE LastName = $1") .bind("p1") @@ -407,7 +447,8 @@ static void writeWithTransactionUsingDml(DatabaseClient dbClient) { // 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"; + "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()) { @@ -419,7 +460,8 @@ static void writeWithTransactionUsingDml(DatabaseClient dbClient) { long transfer = 200000; if (album2Budget >= transfer) { String sql2 = - "SELECT marketingbudget as \"MarketingBudget\" from Albums WHERE SingerId = 1 and AlbumId = 1"; + "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()) { diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java index 9e586cf79bc..5877bd5206c 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java @@ -1,20 +1,23 @@ package com.example.spanner; -import com.google.cloud.Timestamp; -import com.google.cloud.spanner.*; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +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 static com.google.common.truth.Truth.assertThat; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:abbreviationaswordinname") @@ -50,7 +53,8 @@ static void deleteStaleTestDatabases() { 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()) { + if (samplePattern.matcher(toComparableId(PgSpannerSampleIT.baseDbId, + db.getId().getDatabase())).matches()) { db.drop(); } if (restoredPattern.matcher(toComparableId("restored", db.getId().getDatabase())) @@ -171,6 +175,7 @@ public void testSample() throws Exception { System.out.println("Read only transaction ..."); out = runSample("readonlytransaction"); - assertThat(out.replaceAll("[\r\n]+", " ")).containsMatch("(Total Junk.*){2}"); + assertThat(out.replaceAll("[\r\n]+", " ")) + .containsMatch("(Total Junk.*){2}"); } } From 2759e7aeb48c4e6fb7e898a252b83ae6dc7b64a8 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Mon, 28 Mar 2022 16:17:37 +0530 Subject: [PATCH 07/13] docs: samples checkstyle fixes --- .../com/example/spanner/PgSpannerSample.java | 39 +++++++++++++------ .../example/spanner/PgSpannerSampleIT.java | 23 +++++++++-- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java index 16a1b172d73..14f755df06b 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -35,13 +35,28 @@ import com.google.cloud.spanner.Struct; import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; +/** + * 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 PgSpannerSample { static final List SINGERS = Arrays.asList( @@ -244,9 +259,9 @@ static void queryMarketingBudget(DatabaseClient dbClient) { try (ResultSet resultSet = dbClient .singleUse() - .executeQuery(Statement.of("SELECT singerid as \"SingerId\", " + - "albumid as \"AlbumId\", marketingbudget as \"MarketingBudget\" " + - "FROM Albums"))) { + .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", @@ -383,8 +398,8 @@ 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"))) { + .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", @@ -420,8 +435,8 @@ static void writeUsingDml(DatabaseClient dbClient) { static void queryWithParameter(DatabaseClient dbClient) { Statement statement = Statement.newBuilder( - "SELECT singerid AS \"SingerId\", " + - "firstname as \"FirstName\", lastname as \"LastName\" " + "SELECT singerid AS \"SingerId\", " + + "firstname as \"FirstName\", lastname as \"LastName\" " + "FROM Singers " + "WHERE LastName = $1") .bind("p1") @@ -447,8 +462,8 @@ static void writeWithTransactionUsingDml(DatabaseClient dbClient) { // 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"; + "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()) { @@ -460,8 +475,8 @@ static void writeWithTransactionUsingDml(DatabaseClient dbClient) { long transfer = 200000; if (album2Budget >= transfer) { String sql2 = - "SELECT marketingbudget as \"MarketingBudget\" from Albums WHERE " + - "SingerId = 1 and AlbumId = 1"; + "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()) { diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java index 5877bd5206c..e15c137056b 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java @@ -1,3 +1,19 @@ +/* + * 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; @@ -19,6 +35,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +/** Unit tests for {@code PgSpannerSample} */ @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:abbreviationaswordinname") public class PgSpannerSampleIT { @@ -95,9 +112,9 @@ static String formatForTest(String name) { } private String runSample(String command) { - PrintStream stdOut = System.out; - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - PrintStream out = new PrintStream(bout); + 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}); From c4c23892d635d4840c0ff09f36afcd6d4daedf5c Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Tue, 5 Apr 2022 20:11:05 +0530 Subject: [PATCH 08/13] docs: add samples for postgresql --- samples/snippets/pom.xml | 2 +- .../spanner/AsyncQueryToListAsyncExample.java | 3 + .../CustomTimeoutAndRetrySettingsExample.java | 2 +- .../PgAsyncQueryToListAsyncExample.java | 89 ++ .../example/spanner/PgAsyncRunnerExample.java | 134 +++ .../PgAsyncTransactionManagerExample.java | 148 +++ .../PgQueryWithNumericParameterSample.java | 60 ++ .../com/example/spanner/PgSpannerSample.java | 903 +++++++++++++++++- .../spanner/StatementTimeoutExample.java | 2 +- .../example/spanner/PgAsyncExamplesIT.java | 251 +++++ .../example/spanner/PgSpannerSampleIT.java | 105 +- .../PgSpannerStandaloneExamplesIT.java | 209 ++++ 12 files changed, 1903 insertions(+), 5 deletions(-) create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 74aaf074d52..fad81b50655 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -146,7 +146,7 @@ 3.0.0-M5 - spanner-testing-east1 + pg-enabled-spanner spanner-mr-testing nam6 us-east1 diff --git a/samples/snippets/src/main/java/com/example/spanner/AsyncQueryToListAsyncExample.java b/samples/snippets/src/main/java/com/example/spanner/AsyncQueryToListAsyncExample.java index 57082ba3e05..11da6a13fdd 100644 --- a/samples/snippets/src/main/java/com/example/spanner/AsyncQueryToListAsyncExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/AsyncQueryToListAsyncExample.java @@ -31,6 +31,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +/** + * Example code for using Async query on Cloud Spanner and convert it to list. + */ class AsyncQueryToListAsyncExample { static class Album { final long singerId; diff --git a/samples/snippets/src/main/java/com/example/spanner/CustomTimeoutAndRetrySettingsExample.java b/samples/snippets/src/main/java/com/example/spanner/CustomTimeoutAndRetrySettingsExample.java index e3e51875141..7a4a806fdee 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CustomTimeoutAndRetrySettingsExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CustomTimeoutAndRetrySettingsExample.java @@ -80,7 +80,7 @@ static void executeSqlWithCustomTimeoutAndRetrySettings( .readWriteTransaction() .run(transaction -> { String sql = - "INSERT Singers (SingerId, FirstName, LastName)\n" + "INSERT INTO Singers (SingerId, FirstName, LastName)\n" + "VALUES (20, 'George', 'Washington')"; long rowCount = transaction.executeUpdate(Statement.of(sql)); System.out.printf("%d record inserted.%n", rowCount); diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java new file mode 100644 index 00000000000..c670c9b4133 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java @@ -0,0 +1,89 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +// [START spanner_pg_async_query_to_list] +import com.google.api.core.ApiFuture; +import com.google.cloud.spanner.AsyncResultSet; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class PgAsyncQueryToListAsyncExample { + static class Album { + final long singerId; + final long albumId; + final String albumTitle; + + Album(long singerId, long albumId, String albumTitle) { + this.singerId = singerId; + this.albumId = albumId; + this.albumTitle = albumTitle; + } + } + + static void asyncQueryToList() throws InterruptedException, ExecutionException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + asyncQueryToList(client); + } + } + + // Execute a query asynchronously and transform the result to a list. + static void asyncQueryToList(DatabaseClient client) + throws InterruptedException, ExecutionException, TimeoutException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + ApiFuture> albums; + try (AsyncResultSet resultSet = + client + .singleUse() + .executeQueryAsync(Statement.of("SELECT singerid as \"SingerId\", " + + "albumid as \"AlbumId\", albumtitle as \"AlbumTitle\" " + + "FROM Albums"))) { + // Convert the result set to a list of Albums asynchronously. + albums = + resultSet.toListAsync( + reader -> { + return new AsyncQueryToListAsyncExample.Album( + reader.getLong("SingerId"), + reader.getLong("AlbumId"), + reader.getString("AlbumTitle")); + }, + executor); + } + + for (AsyncQueryToListAsyncExample.Album album : albums.get(30L, TimeUnit.SECONDS)) { + System.out.printf("%d %d %s%n", album.singerId, album.albumId, album.albumTitle); + } + executor.shutdown(); + } +} diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java new file mode 100644 index 00000000000..dac8b790d70 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java @@ -0,0 +1,134 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +//[START spanner_async_read_write_transaction] +import com.google.api.core.ApiFunction; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.cloud.spanner.AsyncRunner; +import com.google.cloud.spanner.AsyncRunner.AsyncWork; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.Struct; +import com.google.cloud.spanner.TransactionContext; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.MoreExecutors; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +class PgAsyncRunnerExample { + + static void asyncRunner() throws InterruptedException, ExecutionException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + asyncRunner(client); + } + } + + // Execute a read/write transaction asynchronously. + static void asyncRunner(DatabaseClient client) + throws InterruptedException, ExecutionException, TimeoutException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + + // Create an async transaction runner. + AsyncRunner runner = client.runAsync(); + // The transaction returns the total number of rows that were updated as a future array of + // longs. + ApiFuture rowCounts = + runner.runAsync( + txn -> { + // Transfer marketing budget from one album to another. We do it in a + // transaction to ensure that the transfer is atomic. + ApiFuture album1BudgetFut = + txn.readRowAsync("Albums", Key.of(1, 1), ImmutableList.of("MarketingBudget")); + ApiFuture album2BudgetFut = + txn.readRowAsync("Albums", Key.of(2, 2), ImmutableList.of("MarketingBudget")); + + try { + // Transaction will only be committed if this condition still holds at the + // time of commit. Otherwise it will be aborted and the AsyncWork will be + // rerun by the client library. + long transfer = 200_000; + if (album2BudgetFut.get().getLong(0) >= transfer) { + long album1Budget = album1BudgetFut.get().getLong(0); + long album2Budget = album2BudgetFut.get().getLong(0); + + album1Budget += transfer; + album2Budget -= transfer; + Statement updateStatement1 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1 " + + "WHERE SingerId = 1 and AlbumId = 1") + .bind("p1") + .to(album1Budget) + .build(); + Statement updateStatement2 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1 " + + "WHERE SingerId = 2 and AlbumId = 2") + .bind("p1") + .to(album2Budget) + .build(); + return txn.batchUpdateAsync( + ImmutableList.of(updateStatement1, updateStatement2)); + } else { + return ApiFutures.immediateFuture(new long[] {0L, 0L}); + } + } catch (ExecutionException e) { + throw SpannerExceptionFactory.newSpannerException(e.getCause()); + } catch (InterruptedException e) { + throw SpannerExceptionFactory.propagateInterrupt(e); + } + }, + executor); + + ApiFuture totalUpdateCount = + ApiFutures.transform( + rowCounts, + new ApiFunction() { + @SuppressFBWarnings("UVA_USE_VAR_ARGS") + @Override + public Long apply(long[] input) { + return Arrays.stream(input).sum(); + } + }, + MoreExecutors.directExecutor()); + System.out.printf("%d records updated.%n", totalUpdateCount.get(30L, TimeUnit.SECONDS)); + executor.shutdown(); + } +} +//[END spanner_async_read_write_transaction] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java new file mode 100644 index 00000000000..9f864272e17 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java @@ -0,0 +1,148 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +//[START spanner_async_transaction_manager] +import com.google.api.core.ApiFunction; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.cloud.spanner.AbortedException; +import com.google.cloud.spanner.AsyncTransactionManager; +import com.google.cloud.spanner.AsyncTransactionManager.AsyncTransactionStep; +import com.google.cloud.spanner.AsyncTransactionManager.CommitTimestampFuture; +import com.google.cloud.spanner.AsyncTransactionManager.TransactionContextFuture; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.Struct; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.MoreExecutors; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +class PgAsyncTransactionManagerExample { + + static void asyncTransactionManager() + throws InterruptedException, ExecutionException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + asyncTransactionManager(client); + } + } + + static void asyncTransactionManager(DatabaseClient client) + throws InterruptedException, ExecutionException, TimeoutException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + + AsyncTransactionStep, long[]> updateCounts; + try (AsyncTransactionManager mgr = client.transactionManagerAsync()) { + TransactionContextFuture txn = mgr.beginAsync(); + // Loop to retry aborted errors. + while (true) { + try { + updateCounts = + txn.then( + (transaction, v) -> { + // Execute two reads in parallel and return the result of these as the input + // for the next step of the transaction. + ApiFuture album1BudgetFut = + transaction.readRowAsync( + "Albums", Key.of(1, 1), ImmutableList.of("MarketingBudget")); + ApiFuture album2BudgetFut = + transaction.readRowAsync( + "Albums", Key.of(2, 2), ImmutableList.of("MarketingBudget")); + return ApiFutures.allAsList( + Arrays.asList(album1BudgetFut, album2BudgetFut)); + }, + executor) + // The input of the next step of the transaction is the return value of the + // previous step, i.e. a list containing the marketing budget of two Albums. + .then( + (transaction, budgets) -> { + long album1Budget = budgets.get(0).getLong(0); + long album2Budget = budgets.get(1).getLong(0); + long transfer = 200_000; + if (album2Budget >= transfer) { + album1Budget += transfer; + album2Budget -= transfer; + Statement updateStatement1 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1 " + + "WHERE SingerId = 1 and AlbumId = 1") + .bind("p1") + .to(album1Budget) + .build(); + Statement updateStatement2 = + Statement.newBuilder( + "UPDATE Albums " + + "SET MarketingBudget = $1 " + + "WHERE SingerId = 2 and AlbumId = 2") + .bind("p1") + .to(album2Budget) + .build(); + return transaction.batchUpdateAsync( + ImmutableList.of(updateStatement1, updateStatement2)); + } else { + return ApiFutures.immediateFuture(new long[] {0L, 0L}); + } + }, + executor); + // Commit after the updates. + CommitTimestampFuture commitTsFut = updateCounts.commitAsync(); + // Wait for the transaction to finish and execute a retry if necessary. + commitTsFut.get(); + break; + } catch (AbortedException e) { + txn = mgr.resetForRetryAsync(); + } + } + } + + // Calculate the total update count. + ApiFuture totalUpdateCount = + ApiFutures.transform( + updateCounts, + new ApiFunction() { + @SuppressFBWarnings("UVA_USE_VAR_ARGS") + @Override + public Long apply(long[] input) { + return Arrays.stream(input).sum(); + } + }, + MoreExecutors.directExecutor()); + System.out.printf("%d records updated.%n", totalUpdateCount.get(30L, TimeUnit.SECONDS)); + executor.shutdown(); + } +} +//[END spanner_async_transaction_manager] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java b/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java new file mode 100644 index 00000000000..280ee013ce9 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java @@ -0,0 +1,60 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +// [START spanner_query_with_numeric_parameter] +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.Value; + +class PgQueryWithNumericParameterSample { + + static void queryWithNumericParameter() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + queryWithNumericParameter(client); + } + } + + static void queryWithNumericParameter(DatabaseClient client) { + Statement statement = + Statement.newBuilder( + "SELECT venueid as \"VenueId\", revenue as \"Revenue\" FROM Venues WHERE Revenue " + + "< $1") + .bind("p1") + .to(Value.pgNumeric("100000")) + .build(); + try (ResultSet resultSet = client.singleUse().executeQuery(statement)) { + while (resultSet.next()) { + System.out.printf( + "%d %s%n", resultSet.getLong("VenueId"), resultSet.getValue("Revenue")); + } + } + } +} +// [END spanner_query_with_numeric_parameter] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java index 14f755df06b..8b365f0d184 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -17,29 +17,49 @@ package com.example.spanner; import com.google.api.gax.longrunning.OperationFuture; +import com.google.api.gax.paging.Page; +import com.google.cloud.ByteArray; +import com.google.cloud.Date; +import com.google.cloud.Timestamp; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseAdminClient; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceId; import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ReadOnlyTransaction; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerBatchUpdateException; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.Struct; +import com.google.cloud.spanner.TimestampBound; +import com.google.cloud.spanner.Value; +import com.google.common.io.BaseEncoding; +import com.google.longrunning.Operation; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.spanner.admin.database.v1.CreateBackupMetadata; import com.google.spanner.admin.database.v1.CreateDatabaseMetadata; +import com.google.spanner.admin.database.v1.OptimizeRestoredDatabaseMetadata; import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import com.google.spanner.v1.ExecuteSqlRequest; +import java.math.BigDecimal; import java.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 API. This example demonstrates all the common operations @@ -72,6 +92,100 @@ public class PgSpannerSample { new Album(2, 1, "Green"), new Album(2, 2, "Forever Hold Your Peace"), new Album(2, 3, "Terrified")); + // [START spanner_insert_data_with_timestamp_column] + static final List PERFORMANCES = + Arrays.asList( + new SpannerSample.Performance(1, 4, "2017-10-05", 11000), + new SpannerSample.Performance(1, 19, "2017-11-02", 15000), + new SpannerSample.Performance(2, 42, "2017-12-23", 7000)); + // [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"))); + // [END spanner_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 PgSpannerSample.Venue( + 4, + "Venue 4", + exampleBytes1, + 1800, + availableDates1, + "2018-09-02", + false, + 0.85543f, + new BigDecimal("215100.10")), + new PgSpannerSample.Venue( + 19, + "Venue 19", + exampleBytes2, + 6300, + availableDates2, + "2019-01-15", + true, + 0.98716f, + new BigDecimal("1200100.00")), + new PgSpannerSample.Venue( + 42, + "Venue 42", + exampleBytes3, + 3000, + availableDates3, + "2018-10-01", + false, + 0.72598f, + new BigDecimal("390650.99"))); + // [END spanner_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_create_database] static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { @@ -122,6 +236,31 @@ static void writeExampleData(DatabaseClient dbClient) { } // [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 = @@ -544,9 +683,661 @@ static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseId id } // [END spanner_create_table_using_ddl] + // [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(2)); + } + } + } + // [END spanner_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_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(Timestamp.now()) + .build(), + Mutation.newUpdateBuilder("Albums") + .set("SingerId") + .to(2) + .set("AlbumId") + .to(2) + .set("MarketingBudget") + .to(750000) + .set("LastUpdateTime") + .to(Timestamp.now()) + .build()); + // This writes all the mutations to Cloud Spanner atomically. + dbClient.write(mutations); + } + // [END spanner_update_data_with_timestamp_column] + + // [START spanner_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 TIMESTAMPTZ"), + 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_add_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 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_query_data_with_timestamp_column] + + // [START spanner_create_table_with_timestamp_column] + static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, DatabaseId id) { + OperationFuture op = + dbAdminClient.updateDatabaseDdl( + id.getInstanceId().getInstance(), + id.getDatabase(), + Arrays.asList( + "CREATE TABLE Performances (" + + " SingerId BIGINT NOT NULL," + + " VenueId BIGINT NOT NULL," + + " Revenue BIGINT, " + + " LastUpdateTime TIMESTAMPTZ 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_create_table_with_timestamp_column] + + // [START spanner_insert_data_with_timestamp_column] + static void writeExampleDataWithTimestamp(DatabaseClient dbClient) { + List mutations = new ArrayList<>(); + for (SpannerSample.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(Timestamp.now()) + .build()); + } + dbClient.write(mutations); + } + // [END spanner_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_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] + + // [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_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_dml_write_then_read] + + // [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 BIGINT NOT NULL," + + " VenueName character varying(100)," + + " VenueInfo bytea," + + " Capacity BIGINT," + + " OutdoorVenue BOOL, " + + " PopularityScore FLOAT8, " + + " Revenue NUMERIC, " + + " LastUpdateTime TIMESTAMPTZ 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_create_table_with_datatypes] + + // [START spanner_insert_datatypes_data] + static void writeDatatypesData(DatabaseClient dbClient) { + List mutations = new ArrayList<>(); + for (PgSpannerSample.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(Timestamp.now()) + .build()); + } + dbClient.write(mutations); + } + // [END spanner_insert_datatypes_data] + + // [START spanner_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_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 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_query_with_bytes_parameter] + + // [START spanner_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_query_with_float_parameter] + + // [START spanner_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_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 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_query_with_string_parameter] + + // [START spanner_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_query_with_timestamp_parameter] + + // [START spanner_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_query_with_numeric_parameter] + + // [START spanner_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_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(ExecuteSqlRequest.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_list_backup_operations] + static void listBackupOperations(InstanceAdminClient instanceAdminClient, DatabaseId databaseId) { + Instance instance = instanceAdminClient.getInstance(databaseId.getInstanceId().getInstance()); + // Get create backup operations for the sample database. + Timestamp last24Hours = Timestamp.ofTimeSecondsAndNanos(TimeUnit.SECONDS.convert( + TimeUnit.HOURS.convert(Timestamp.now().getSeconds(), TimeUnit.SECONDS) - 24, + TimeUnit.HOURS), 0); + String filter = + String.format( + "(metadata.database:%s) AND " + + "(metadata.@type:type.googleapis.com/" + + "google.spanner.admin.database.v1.CreateBackupMetadata) AND " + + "(metadata.progress.start_time > \"%s\")", + databaseId.getName(), last24Hours); + Page operations = instance + .listBackupOperations(Options.filter(filter)); + for (com.google.longrunning.Operation op : operations.iterateAll()) { + try { + CreateBackupMetadata metadata = op.getMetadata().unpack(CreateBackupMetadata.class); + System.out.println( + String.format( + "Backup %s on database %s pending: %d%% complete", + metadata.getName(), + metadata.getDatabase(), + metadata.getProgress().getProgressPercent())); + } catch (InvalidProtocolBufferException e) { + // The returned operation does not contain CreateBackupMetadata. + System.err.println(e.getMessage()); + } + } + } + // [END spanner_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] + static void run( DatabaseClient dbClient, DatabaseAdminClient dbAdminClient, + InstanceAdminClient instanceAdminClient, String command, DatabaseId database) { switch (command) { @@ -556,6 +1347,9 @@ static void run( case "write": writeExampleData(dbClient); break; + case "delete": + deleteExampleData(dbClient); + break; case "query": query(dbClient); break; @@ -604,6 +1398,90 @@ static void run( 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 "writestructdata": + writeStructExampleData(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(); } @@ -633,6 +1511,28 @@ static void printUsageAndExit() { 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); } @@ -660,10 +1560,11 @@ public static void main(String[] args) { // [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, command, db); + run(dbClient, dbAdminClient, instanceAdminClient, command, db); // [START spanner_init_client] } finally { spanner.close(); diff --git a/samples/snippets/src/main/java/com/example/spanner/StatementTimeoutExample.java b/samples/snippets/src/main/java/com/example/spanner/StatementTimeoutExample.java index 6c45216b317..2a82467754f 100644 --- a/samples/snippets/src/main/java/com/example/spanner/StatementTimeoutExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/StatementTimeoutExample.java @@ -65,7 +65,7 @@ public ApiCallContext configure(ApiCallContext context, ReqT reque // Run the transaction in the custom context. context.run(() -> client.readWriteTransaction().run(transaction -> { - String sql = "INSERT Singers (SingerId, FirstName, LastName)\n" + String sql = "INSERT INTO Singers (SingerId, FirstName, LastName)\n" + "VALUES (20, 'George', 'Washington')"; long rowCount = transaction.executeUpdate(Statement.of(sql)); System.out.printf("%d record inserted.%n", rowCount); diff --git a/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java b/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java new file mode 100644 index 00000000000..8a0c9c2b82c --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java @@ -0,0 +1,251 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +import static com.example.spanner.SampleRunner.runSample; +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.KeySet; +import com.google.cloud.spanner.Mutation; +import com.google.common.collect.ImmutableList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Integration tests for Cloud Spanner Async API examples for Postgresql. + */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class PgAsyncExamplesIT extends SampleTestBase { + + private static DatabaseId databaseId; + + @BeforeClass + public static void createTestDatabase() throws Exception { + final String database = idGenerator.generateDatabaseId(); + databaseId = DatabaseId.of(projectId, instanceId, database); + databaseAdminClient + .createDatabase( + databaseAdminClient + .newDatabaseBuilder(databaseId) + .setDialect(Dialect.POSTGRESQL).build(), + Collections.emptyList()) + .get(); + databaseAdminClient.updateDatabaseDdl( + instanceId, + database, + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL," + + " FirstName character varying(1024)," + + " LastName character varying(1024)," + + " SingerInfo bytea," + + " PRIMARY KEY (SingerId)" + + ")", + "CREATE TABLE Albums (" + + " SingerId bigint NOT NULL," + + " AlbumId bigint NOT NULL," + + " AlbumTitle character varying(1024)," + + " MarketingBudget bigint," + + " PRIMARY KEY (SingerId, AlbumId)" + + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE", + "CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"), + null).get(); + } + + static class Singer { + + final long singerId; + final String firstName; + final String lastName; + + Singer(long singerId, String firstName, String lastName) { + this.singerId = singerId; + this.firstName = firstName; + this.lastName = lastName; + } + } + + static class Album { + + final long singerId; + final long albumId; + final String albumTitle; + final Long marketingBudget; + + Album(long singerId, long albumId, String albumTitle, Long marketingBudget) { + this.singerId = singerId; + this.albumId = albumId; + this.albumTitle = albumTitle; + this.marketingBudget = marketingBudget; + } + } + + private static final List TEST_SINGERS = + Arrays.asList( + new Singer(1, "Marc", "Richards"), + new Singer(2, "Catalina", "Smith"), + new Singer(3, "Alice", "Trentor"), + new Singer(4, "Lea", "Martin"), + new Singer(5, "David", "Lomond")); + private static final List ALBUMS = + Arrays.asList( + new Album(1, 1, "Total Junk", 300_000L), + new Album(1, 2, "Go, Go, Go", 400_000L), + new Album(2, 1, "Green", 150_000L), + new Album(2, 2, "Forever Hold Your Peace", 350_000L), + new Album(2, 3, "Terrified", null)); + + @Before + public void insertTestData() { + DatabaseClient client = spanner.getDatabaseClient(databaseId); + ImmutableList.Builder mutations = + ImmutableList.builderWithExpectedSize(TEST_SINGERS.size()); + for (Singer singer : TEST_SINGERS) { + mutations.add( + Mutation.newInsertBuilder("Singers") + .set("SingerId") + .to(singer.singerId) + .set("FirstName") + .to(singer.firstName) + .set("LastName") + .to(singer.lastName) + .build()); + } + for (Album album : ALBUMS) { + mutations.add( + Mutation.newInsertBuilder("Albums") + .set("SingerId") + .to(album.singerId) + .set("AlbumId") + .to(album.albumId) + .set("AlbumTitle") + .to(album.albumTitle) + .set("MarketingBudget") + .to(album.marketingBudget) + .build()); + } + client.write(mutations.build()); + } + + private void assertSingersOutput(String out) { + assertThat(out).contains("1 Marc Richard"); + assertThat(out).contains("2 Catalina Smith"); + assertThat(out).contains("3 Alice Trentor"); + assertThat(out).contains("4 Lea Martin"); + assertThat(out).contains("5 David Lomond"); + } + + private void assertAlbumsOutput(String out) { + assertThat(out).contains("1 1 Total Junk"); + assertThat(out).contains("1 2 Go, Go, Go"); + assertThat(out).contains("2 1 Green"); + assertThat(out).contains("2 2 Forever Hold Your Peace"); + assertThat(out).contains("2 3 Terrified"); + } + + @After + public void removeTestData() { + DatabaseClient client = spanner.getDatabaseClient(databaseId); + client.write(Arrays.asList(Mutation.delete("Singers", KeySet.all()))); + } + + @Test + public void asyncQuery_shouldReturnData() throws Exception { + String out = runSample( + () -> AsyncQueryExample.asyncQuery(spanner.getDatabaseClient(databaseId))); + assertAlbumsOutput(out); + } + + @Test + public void asyncQueryToListAsync_shouldReturnData() + throws Exception { + String out = runSample( + () -> PgAsyncQueryToListAsyncExample + .asyncQueryToList(spanner.getDatabaseClient(databaseId))); + assertAlbumsOutput(out); + } + + @Test + public void asyncRead_shouldReturnData() + throws Exception { + String out = runSample(() -> AsyncReadExample.asyncRead(spanner.getDatabaseClient(databaseId))); + assertAlbumsOutput(out); + } + + @Test + public void asyncReadUsingIndex_shouldReturnDataInCorrectOrder() throws Exception { + String out = runSample(() -> AsyncReadUsingIndexExample + .asyncReadUsingIndex(spanner.getDatabaseClient(databaseId))); + assertThat(out) + .contains( + "2 Forever Hold Your Peace\n" + + "2 Go, Go, Go\n" + + "1 Green\n" + + "3 Terrified\n" + + "1 Total Junk"); + } + + @Test + public void asyncReadOnlyTransaction_shouldReturnData() throws Exception { + String out = runSample(() -> AsyncReadOnlyTransactionExample + .asyncReadOnlyTransaction(spanner.getDatabaseClient(databaseId))); + assertAlbumsOutput(out); + assertSingersOutput(out); + } + + @Test + public void asyncDml_shouldInsertRows() throws Exception { + String out = runSample(() -> AsyncDmlExample.asyncDml(spanner.getDatabaseClient(databaseId))); + assertThat(out).contains("4 records inserted."); + } + + @Test + public void asyncRunner_shouldUpdateRows() throws Exception { + String out = runSample( + () -> PgAsyncRunnerExample.asyncRunner(spanner.getDatabaseClient(databaseId))); + assertThat(out).contains("2 records updated."); + } + + @Test + public void asyncTransactionManager_shouldUpdateRows() throws Exception { + String out = runSample(() -> PgAsyncTransactionManagerExample + .asyncTransactionManager(spanner.getDatabaseClient(databaseId))); + assertThat(out).contains("2 records updated."); + } + + @Test + public void asyncReadRow_shouldPrintRow() throws Exception { + String out = runSample( + () -> AsyncReadRowExample.asyncReadRow(spanner.getDatabaseClient(databaseId))); + assertThat(out).contains("1 1 Total Junk"); + assertThat(out).doesNotContain("1 2 Go, Go, Go"); + assertThat(out).doesNotContain("2 1 Green"); + assertThat(out).doesNotContain("2 2 Forever Hold Your Peace"); + assertThat(out).doesNotContain("2 3 Terrified"); + } +} diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java index e15c137056b..6a395b99171 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java @@ -35,7 +35,9 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Unit tests for {@code PgSpannerSample} */ +/** + * Unit tests for {@code PgSpannerSample} + */ @RunWith(JUnit4.class) @SuppressWarnings("checkstyle:abbreviationaswordinname") public class PgSpannerSampleIT { @@ -194,5 +196,106 @@ public void testSample() throws Exception { out = runSample("readonlytransaction"); assertThat(out.replaceAll("[\r\n]+", " ")) .containsMatch("(Total Junk.*){2}"); + + out = runSample("addlastupdatetimestampcolumn"); + assertThat(out).contains("Added LastUpdateTime as a 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"); + + System.out.println("Write with Timestamp ..."); + runSample("writewithtimestamp"); + out = runSample("queryperformancestable"); + assertThat(out).contains("1 4 11000"); + assertThat(out).contains("1 19 15000"); + assertThat(out).contains("2 42 7000"); + + System.out.println("Write using DML ..."); + runSample("insertusingdml"); + out = runSample("querysingerstable"); + assertThat(out).contains("Virginia Watson"); + + System.out.println("Update using DML ..."); + runSample("updateusingdml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("1 1 2000000"); + + System.out.println("Delete using DML ..."); + runSample("deleteusingdml"); + out = runSample("querysingerstable"); + assertThat(out).doesNotContain("Alice Trentor"); + + System.out.println("Write and Read using DML ..."); + out = runSample("writeandreadusingdml"); + assertThat(out).contains("Timothy Campbell"); + + System.out.println("Update using partitioned DML ..."); + runSample("updateusingpartitioneddml"); + out = runSample("querymarketingbudget"); + assertThat(out).contains("2 2 100000"); + assertThat(out).contains("1 1 2000000"); + + System.out.println("Delete using Partitioned DML ..."); + runSample("deleteusingpartitioneddml"); + out = runSample("querysingerstable"); + assertThat(out).doesNotContain("Timothy Grant"); + assertThat(out).doesNotContain("Melissa Garcia"); + assertThat(out).doesNotContain("Russell Morales"); + assertThat(out).doesNotContain("Jacqueline Long"); + assertThat(out).doesNotContain("Dylan Shaw"); + + System.out.println("Update in Batch using DML ..."); + out = runSample("updateusingbatchdml"); + assertThat(out).contains("1 record updated by stmt 0"); + assertThat(out).contains("1 record updated by stmt 1"); + + System.out.println("Create table with data types ..."); + out = runSample("createtablewithdatatypes"); + assertThat(out).contains("Created Venues table in database"); + + System.out.println("Write into table and Query Boolean Type ..."); + runSample("writedatatypesdata"); + out = runSample("querywithbool"); + assertThat(out).contains("19 Venue 19 true"); + + System.out.println("Query with Bytes ..."); + out = runSample("querywithbytes"); + assertThat(out).contains("4 Venue 4"); + + System.out.println("Query with Float ..."); + out = runSample("querywithfloat"); + assertThat(out).contains("4 Venue 4 0.8"); + assertThat(out).contains("19 Venue 19 0.9"); + + System.out.println("Query with Int ..."); + out = runSample("querywithint"); + assertThat(out).contains("19 Venue 19 6300"); + assertThat(out).contains("42 Venue 42 3000"); + + System.out.println("Query with String ..."); + out = runSample("querywithstring"); + assertThat(out).contains("42 Venue 42"); + + System.out.println("Query with Timestamp parameter ..."); + out = runSample("querywithtimestampparameter"); + assertThat(out).contains("4 Venue 4"); + assertThat(out).contains("19 Venue 19"); + assertThat(out).contains("42 Venue 42"); + + System.out.println("Query with Numeric Type ..."); + out = runSample("querywithnumeric"); + assertThat(out).contains("19 Venue 19 1200100"); + assertThat(out).contains("42 Venue 42 390650.99"); + + System.out.println("Query options ..."); + out = runSample("clientwithqueryoptions"); + assertThat(out).contains("1 1 Total Junk"); + out = runSample("querywithqueryoptions"); + assertThat(out).contains("1 1 Total Junk"); } } diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java new file mode 100644 index 00000000000..43c9c949f19 --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java @@ -0,0 +1,209 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.spanner; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.KeySet; +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.common.collect.ImmutableList; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.concurrent.ExecutionException; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Integration tests for Cloud Spanner cloud client examples. */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class PgSpannerStandaloneExamplesIT { + // The instance needs to exist for tests to pass. + private static String instanceId = System.getProperty("spanner.test.instance"); + private static String baseDatabaseId = System.getProperty("spanner.sample.database", "mysample"); + private static String databaseId = SpannerSampleIT.formatForTest(baseDatabaseId); + private static DatabaseId dbId; + private static DatabaseAdminClient dbClient; + private static Spanner spanner; + + private String runExample(Runnable example) { + PrintStream stdOut = System.out; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(bout); + System.setOut(out); + example.run(); + System.setOut(stdOut); + return bout.toString(); + } + + @BeforeClass + public static void createTestDatabase() throws Exception { + SpannerOptions options = + SpannerOptions.newBuilder().setAutoThrottleAdministrativeRequests().build(); + spanner = options.getService(); + dbClient = spanner.getDatabaseAdminClient(); + if (instanceId == null) { + Iterator iterator = + spanner.getInstanceAdminClient().listInstances().iterateAll().iterator(); + if (iterator.hasNext()) { + instanceId = iterator.next().getId().getInstance(); + } + } + dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); + dbClient + .createDatabase( + dbClient.newDatabaseBuilder(dbId).setDialect(Dialect.POSTGRESQL).build(), + Collections.emptyList()) + .get(); + dbClient.updateDatabaseDdl( + instanceId, + databaseId, + Arrays.asList( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL," + + " FirstName character varying(1024)," + + " LastName character varying(1024)," + + " SingerInfo bytea," + + " PRIMARY KEY (SingerId)" + + ")", + "CREATE TABLE Venues (" + + "VenueId bigint NOT NULL," + + "Revenue NUMERIC," + + "PRIMARY KEY (VenueId))"), + null).get(); + } + + @AfterClass + public static void dropTestDatabase() throws Exception { + dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); + spanner.close(); + } + + @Before + public void deleteTestData() { + String projectId = spanner.getOptions().getProjectId(); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + client.write(Collections.singleton(Mutation.delete("Singers", KeySet.all()))); + client.write(Collections.singleton(Mutation.delete("Venues", KeySet.all()))); + } + + @Test + public void executeSqlWithCustomTimeoutAndRetrySettings_shouldWriteData() { + String projectId = spanner.getOptions().getProjectId(); + String out = + runExample( + () -> + CustomTimeoutAndRetrySettingsExample.executeSqlWithCustomTimeoutAndRetrySettings( + projectId, instanceId, databaseId)); + assertThat(out).contains("1 record inserted."); + } + + @Test + public void executeSqlWithTimeout_shouldWriteData() { + String projectId = spanner.getOptions().getProjectId(); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + String out = runExample(() -> StatementTimeoutExample.executeSqlWithTimeout(client)); + assertThat(out).contains("1 record inserted."); + } + + @Test + public void addNumericColumn_shouldSuccessfullyAddColumn() + throws InterruptedException, ExecutionException { + OperationFuture operation = + spanner + .getDatabaseAdminClient() + .updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of("ALTER TABLE Venues DROP COLUMN Revenue"), + null); + operation.get(); + String out = + runExample( + () -> { + try { + AddNumericColumnSample.addNumericColumn( + spanner.getDatabaseAdminClient(), instanceId, databaseId); + } catch (ExecutionException e) { + System.out.printf( + "Adding column `Revenue` failed: %s%n", e.getCause().getMessage()); + } catch (InterruptedException e) { + System.out.printf("Adding column `Revenue` was interrupted%n"); + } + }); + assertThat(out).contains("Successfully added column `Revenue`"); + } + + @Test + public void updateNumericData_shouldWriteData() { + String projectId = spanner.getOptions().getProjectId(); + String out = + runExample( + () -> + UpdateNumericDataSample.updateNumericData( + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)))); + assertThat(out).contains("Venues successfully updated"); + } + + @Test + public void queryWithNumericParameter_shouldReturnResults() { + String projectId = spanner.getOptions().getProjectId(); + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(projectId, instanceId, databaseId)); + client.write( + ImmutableList.of( + Mutation.newInsertOrUpdateBuilder("Venues") + .set("VenueId") + .to(4L) + .set("Revenue") + .to(new BigDecimal("35000")) + .build(), + Mutation.newInsertOrUpdateBuilder("Venues") + .set("VenueId") + .to(19L) + .set("Revenue") + .to(new BigDecimal("104500")) + .build(), + Mutation.newInsertOrUpdateBuilder("Venues") + .set("VenueId") + .to(42L) + .set("Revenue") + .to(new BigDecimal("99999999999999999999999999999.99")) + .build())); + String out = + runExample(() -> PgQueryWithNumericParameterSample.queryWithNumericParameter(client)); + assertThat(out).contains("4 35000"); + } +} From daede6b32d15d0589d8a9702f126e8980c7fec4a Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Tue, 5 Apr 2022 20:14:48 +0530 Subject: [PATCH 09/13] docs: samples tag fixes --- .../java/com/example/spanner/PgAsyncQueryToListAsyncExample.java | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java index c670c9b4133..4bf1d3fac92 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java @@ -87,3 +87,4 @@ static void asyncQueryToList(DatabaseClient client) executor.shutdown(); } } +//[END spanner_pg_async_query_to_list] From 0a50828e29702829fd5b33451b24824de7bad332 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Mon, 11 Apr 2022 16:03:24 +0530 Subject: [PATCH 10/13] docs: pg samples pr comments fixes --- samples/snippets/pom.xml | 2 +- .../PgAsyncQueryToListAsyncExample.java | 12 +- .../example/spanner/PgAsyncRunnerExample.java | 18 +- .../PgAsyncTransactionManagerExample.java | 16 +- .../PgQueryWithNumericParameterSample.java | 6 +- .../com/example/spanner/PgSpannerSample.java | 307 ++++++++---------- .../example/spanner/PgAsyncExamplesIT.java | 2 +- .../example/spanner/PgSpannerSampleIT.java | 7 +- .../PgSpannerStandaloneExamplesIT.java | 2 +- 9 files changed, 166 insertions(+), 206 deletions(-) diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index fad81b50655..74aaf074d52 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -146,7 +146,7 @@ 3.0.0-M5 - pg-enabled-spanner + spanner-testing-east1 spanner-mr-testing nam6 us-east1 diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java index 4bf1d3fac92..63e7205563b 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google Inc. + * 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. @@ -16,7 +16,7 @@ package com.example.spanner; -// [START spanner_pg_async_query_to_list] +// [START spanner_postgresql_async_query_to_list] import com.google.api.core.ApiFuture; import com.google.cloud.spanner.AsyncResultSet; import com.google.cloud.spanner.DatabaseClient; @@ -62,7 +62,7 @@ static void asyncQueryToList() throws InterruptedException, ExecutionException, static void asyncQueryToList(DatabaseClient client) throws InterruptedException, ExecutionException, TimeoutException { ExecutorService executor = Executors.newSingleThreadExecutor(); - ApiFuture> albums; + ApiFuture> albums; try (AsyncResultSet resultSet = client .singleUse() @@ -73,7 +73,7 @@ static void asyncQueryToList(DatabaseClient client) albums = resultSet.toListAsync( reader -> { - return new AsyncQueryToListAsyncExample.Album( + return new Album( reader.getLong("SingerId"), reader.getLong("AlbumId"), reader.getString("AlbumTitle")); @@ -81,10 +81,10 @@ static void asyncQueryToList(DatabaseClient client) executor); } - for (AsyncQueryToListAsyncExample.Album album : albums.get(30L, TimeUnit.SECONDS)) { + for (Album album : albums.get(30L, TimeUnit.SECONDS)) { System.out.printf("%d %d %s%n", album.singerId, album.albumId, album.albumTitle); } executor.shutdown(); } } -//[END spanner_pg_async_query_to_list] +//[END spanner_postgresql_async_query_to_list] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java index dac8b790d70..f05b509d4c8 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google Inc. + * 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. @@ -16,12 +16,10 @@ package com.example.spanner; -//[START spanner_async_read_write_transaction] -import com.google.api.core.ApiFunction; +//[START spanner_postgresql_async_read_write_transaction] import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.cloud.spanner.AsyncRunner; -import com.google.cloud.spanner.AsyncRunner.AsyncWork; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.Key; @@ -30,10 +28,8 @@ import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.Struct; -import com.google.cloud.spanner.TransactionContext; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.MoreExecutors; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.Arrays; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -119,16 +115,10 @@ static void asyncRunner(DatabaseClient client) ApiFuture totalUpdateCount = ApiFutures.transform( rowCounts, - new ApiFunction() { - @SuppressFBWarnings("UVA_USE_VAR_ARGS") - @Override - public Long apply(long[] input) { - return Arrays.stream(input).sum(); - } - }, + input -> Arrays.stream(input).sum(), MoreExecutors.directExecutor()); System.out.printf("%d records updated.%n", totalUpdateCount.get(30L, TimeUnit.SECONDS)); executor.shutdown(); } } -//[END spanner_async_read_write_transaction] +//[END spanner_postgresql_async_read_write_transaction] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java b/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java index 9f864272e17..d1b20decde6 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google Inc. + * 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. @@ -16,8 +16,7 @@ package com.example.spanner; -//[START spanner_async_transaction_manager] -import com.google.api.core.ApiFunction; +//[START spanner_postgresql_async_transaction_manager] import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.cloud.spanner.AbortedException; @@ -34,7 +33,6 @@ import com.google.cloud.spanner.Struct; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.MoreExecutors; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; @@ -133,16 +131,10 @@ static void asyncTransactionManager(DatabaseClient client) ApiFuture totalUpdateCount = ApiFutures.transform( updateCounts, - new ApiFunction() { - @SuppressFBWarnings("UVA_USE_VAR_ARGS") - @Override - public Long apply(long[] input) { - return Arrays.stream(input).sum(); - } - }, + input -> Arrays.stream(input).sum(), MoreExecutors.directExecutor()); System.out.printf("%d records updated.%n", totalUpdateCount.get(30L, TimeUnit.SECONDS)); executor.shutdown(); } } -//[END spanner_async_transaction_manager] +//[END spanner_postgresql_async_transaction_manager] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java b/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java index 280ee013ce9..144a26def38 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google Inc. + * 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. @@ -16,7 +16,7 @@ package com.example.spanner; -// [START spanner_query_with_numeric_parameter] +// [START spanner_postgresql_query_with_numeric_parameter] import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; import com.google.cloud.spanner.ResultSet; @@ -57,4 +57,4 @@ static void queryWithNumericParameter(DatabaseClient client) { } } } -// [END spanner_query_with_numeric_parameter] +// [END spanner_postgresql_query_with_numeric_parameter] diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java index 8b365f0d184..3679d88f423 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Google Inc. + * 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. @@ -54,6 +54,7 @@ import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; import com.google.spanner.v1.ExecuteSqlRequest; import java.math.BigDecimal; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -62,20 +63,7 @@ import java.util.concurrent.TimeUnit; /** - * Example code for using the Cloud Spanner API. This example demonstrates all the common operations - * that can be done on Cloud Spanner. These are: - * - *

- * - *

    - *
  • Creating a Cloud Spanner database. - *
  • Writing, reading and executing SQL queries. - *
  • Writing data using a read-write transaction. - *
  • Using an index to read and execute SQL queries over data. - *
  • Using commit timestamp for tracking when a record was last updated. - *
  • Using Google API Extensions for Java to make thread-safe requests via long-running - * operations. http://googleapis.github.io/gax-java/ - *
+ * Example code for using the Cloud Spanner PostgreSQL interface. */ public class PgSpannerSample { static final List SINGERS = @@ -92,13 +80,30 @@ public class PgSpannerSample { new Album(2, 1, "Green"), new Album(2, 2, "Forever Hold Your Peace"), new Album(2, 3, "Terrified")); - // [START spanner_insert_data_with_timestamp_column] - static final List PERFORMANCES = + + /** 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 SpannerSample.Performance(1, 4, "2017-10-05", 11000), - new SpannerSample.Performance(1, 19, "2017-11-02", 15000), - new SpannerSample.Performance(2, 42, "2017-12-23", 7000)); - // [START spanner_insert_datatypes_data] + 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( @@ -114,13 +119,13 @@ public class PgSpannerSample { Date.parseDate("2020-11-15"))); static Value availableDates3 = Value.dateArray(Arrays.asList(Date.parseDate("2020-10-01"), Date.parseDate("2020-10-07"))); - // [END spanner_insert_data_with_timestamp_column] + // [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 = + static final List VENUES = Arrays.asList( - new PgSpannerSample.Venue( + new Venue( 4, "Venue 4", exampleBytes1, @@ -130,7 +135,7 @@ public class PgSpannerSample { false, 0.85543f, new BigDecimal("215100.10")), - new PgSpannerSample.Venue( + new Venue( 19, "Venue 19", exampleBytes2, @@ -140,7 +145,7 @@ public class PgSpannerSample { true, 0.98716f, new BigDecimal("1200100.00")), - new PgSpannerSample.Venue( + new Venue( 42, "Venue 42", exampleBytes3, @@ -150,7 +155,7 @@ public class PgSpannerSample { false, 0.72598f, new BigDecimal("390650.99"))); - // [END spanner_insert_datatypes_data] + // [END spanner_postgresql_insert_datatypes_data] /** Class to contain venue sample data. */ static class Venue { @@ -187,8 +192,8 @@ static class Venue { } } - // [START spanner_create_database] - static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { + // [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()); @@ -205,9 +210,9 @@ static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { throw SpannerExceptionFactory.propagateInterrupt(e); } } - // [END spanner_create_database] + // [END spanner_postgresql_create_database] - // [START spanner_insert_data] + // [START spanner_postgresql_insert_data] static void writeExampleData(DatabaseClient dbClient) { List mutations = new ArrayList<>(); for (Singer singer : SINGERS) { @@ -234,9 +239,9 @@ static void writeExampleData(DatabaseClient dbClient) { } dbClient.write(mutations); } - // [END spanner_insert_data] + // [END spanner_postgresql_insert_data] - // [START spanner_delete_data] + // [START spanner_postgresql_delete_data] static void deleteExampleData(DatabaseClient dbClient) { List mutations = new ArrayList<>(); @@ -259,9 +264,9 @@ static void deleteExampleData(DatabaseClient dbClient) { dbClient.write(mutations); System.out.printf("Records deleted.\n"); } - // [END spanner_delete_data] + // [END spanner_postgresql_delete_data] - // [START spanner_query_data] + // [START spanner_postgresql_query_data] static void query(DatabaseClient dbClient) { try (ResultSet resultSet = dbClient @@ -274,9 +279,9 @@ static void query(DatabaseClient dbClient) { } } } - // [END spanner_query_data] + // [END spanner_postgresql_query_data] - // [START spanner_read_data] + // [START spanner_postgresql_read_data] static void read(DatabaseClient dbClient) { try (ResultSet resultSet = dbClient @@ -292,9 +297,9 @@ static void read(DatabaseClient dbClient) { } } } - // [END spanner_read_data] + // [END spanner_postgresql_read_data] - // [START spanner_add_column] + // [START spanner_postgresql_add_column] static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) { OperationFuture op = adminClient.updateDatabaseDdl( dbId.getInstanceId().getInstance(), @@ -314,11 +319,11 @@ static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) throw SpannerExceptionFactory.propagateInterrupt(e); } } - // [END spanner_add_column] + // [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_update_data] + // [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. @@ -343,9 +348,9 @@ static void update(DatabaseClient dbClient) { // This writes all the mutations to Cloud Spanner atomically. dbClient.write(mutations); } - // [END spanner_update_data] + // [END spanner_postgresql_update_data] - // [START spanner_read_write_transaction] + // [START spanner_postgresql_read_write_transaction] static void writeWithTransaction(DatabaseClient dbClient) { dbClient .readWriteTransaction() @@ -388,9 +393,9 @@ static void writeWithTransaction(DatabaseClient dbClient) { return null; }); } - // [END spanner_read_write_transaction] + // [END spanner_postgresql_read_write_transaction] - // [START spanner_query_data_with_new_column] + // [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 @@ -413,9 +418,9 @@ static void queryMarketingBudget(DatabaseClient dbClient) { } } } - // [END spanner_query_data_with_new_column] + // [END spanner_postgresql_query_data_with_new_column] - // [START spanner_create_index] + // [START spanner_postgresql_create_index] static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { OperationFuture op = adminClient.updateDatabaseDdl( @@ -436,9 +441,9 @@ static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { throw SpannerExceptionFactory.propagateInterrupt(e); } } - // [END spanner_create_index] + // [END spanner_postgresql_create_index] - // [START spanner_read_data_with_index] + // [START spanner_postgresql_read_data_with_index] static void readUsingIndex(DatabaseClient dbClient) { try (ResultSet resultSet = dbClient @@ -453,9 +458,9 @@ static void readUsingIndex(DatabaseClient dbClient) { } } } - // [END spanner_read_data_with_index] + // [END spanner_postgresql_read_data_with_index] - // [START spanner_create_storing_index] + // [START spanner_postgresql_create_storing_index] static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { OperationFuture op = adminClient.updateDatabaseDdl( dbId.getInstanceId().getInstance(), @@ -477,11 +482,11 @@ static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) { throw SpannerExceptionFactory.propagateInterrupt(e); } } - // [END spanner_create_storing_index] + // [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) STORING (MarketingBudget)". - // [START spanner_read_data_with_storing_index] + // 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 = @@ -497,14 +502,13 @@ static void readStoringIndex(DatabaseClient dbClient) { "%d %s %s\n", resultSet.getLong(0), resultSet.getString(1), - // TODO: MarketingBudget uppercase not supported, keeping lowercase for now resultSet.isNull("marketingbudget") ? "NULL" : resultSet.getLong(2)); } } } - // [END spanner_read_data_with_storing_index] + // [END spanner_postgresql_read_data_with_storing_index] - // [START spanner_read_only_transaction] + // [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. @@ -530,9 +534,9 @@ static void readOnlyTransaction(DatabaseClient dbClient) { } } } - // [END spanner_read_only_transaction] + // [END spanner_postgresql_read_only_transaction] - // [START spanner_query_singers_table] + // [START spanner_postgresql_query_singers_table] static void querySingersTable(DatabaseClient dbClient) { try (ResultSet resultSet = dbClient @@ -548,10 +552,10 @@ static void querySingersTable(DatabaseClient dbClient) { } } } - // [END spanner_query_singers_table] + // [END spanner_postgresql_query_singers_table] - // [START spanner_dml_getting_started_insert] + // [START spanner_postgresql_dml_getting_started_insert] static void writeUsingDml(DatabaseClient dbClient) { // Insert 4 singer records dbClient @@ -568,9 +572,9 @@ static void writeUsingDml(DatabaseClient dbClient) { return null; }); } - // [END spanner_dml_getting_started_insert] + // [END spanner_postgresql_dml_getting_started_insert] - // [START spanner_query_with_parameter] + // [START spanner_postgresql_query_with_parameter] static void queryWithParameter(DatabaseClient dbClient) { Statement statement = Statement.newBuilder( @@ -591,9 +595,9 @@ static void queryWithParameter(DatabaseClient dbClient) { } } } - // [END spanner_query_with_parameter] + // [END spanner_postgresql_query_with_parameter] - // [START spanner_dml_getting_started_update] + // [START spanner_postgresql_dml_getting_started_update] static void writeWithTransactionUsingDml(DatabaseClient dbClient) { dbClient .readWriteTransaction() @@ -645,9 +649,9 @@ static void writeWithTransactionUsingDml(DatabaseClient dbClient) { return null; }); } - // [END spanner_dml_getting_started_update] + // [END spanner_postgresql_dml_getting_started_update] - // [START spanner_create_table_using_ddl] + // [START spanner_postgresql_create_table_using_ddl] static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseId id) { OperationFuture op = dbAdminClient.updateDatabaseDdl( @@ -681,9 +685,9 @@ static void createTableUsingDdl(DatabaseAdminClient dbAdminClient, DatabaseId id throw SpannerExceptionFactory.propagateInterrupt(e); } } - // [END spanner_create_table_using_ddl] + // [END spanner_postgresql_create_table_using_ddl] - // [START spanner_read_stale_data] + // [START spanner_postgresql_read_stale_data] static void readStaleData(DatabaseClient dbClient) { try (ResultSet resultSet = dbClient @@ -700,13 +704,13 @@ static void readStaleData(DatabaseClient dbClient) { } } } - // [END spanner_read_stale_data] + // [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_update_data_with_timestamp_column] + // [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. @@ -720,7 +724,7 @@ static void updateWithTimestamp(DatabaseClient dbClient) { .set("MarketingBudget") .to(1000000) .set("LastUpdateTime") - .to(Timestamp.now()) + .to(Value.COMMIT_TIMESTAMP) .build(), Mutation.newUpdateBuilder("Albums") .set("SingerId") @@ -730,21 +734,21 @@ static void updateWithTimestamp(DatabaseClient dbClient) { .set("MarketingBudget") .to(750000) .set("LastUpdateTime") - .to(Timestamp.now()) + .to(Value.COMMIT_TIMESTAMP) .build()); // This writes all the mutations to Cloud Spanner atomically. dbClient.write(mutations); } - // [END spanner_update_data_with_timestamp_column] + // [END spanner_postgresql_update_data_with_timestamp_column] - // [START spanner_add_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 TIMESTAMPTZ"), + "ALTER TABLE Albums ADD COLUMN LastUpdateTime spanner.commit_timestamp"), null); try { // Initiate the request which returns an OperationFuture. @@ -759,9 +763,9 @@ static void addLastUpdateTimestampColumn(DatabaseAdminClient adminClient, Databa throw SpannerExceptionFactory.propagateInterrupt(e); } } - // [END spanner_add_timestamp_column] + // [END spanner_postgresql_add_timestamp_column] - // [START spanner_query_data_with_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 @@ -787,9 +791,9 @@ static void queryMarketingBudgetWithTimestamp(DatabaseClient dbClient) { } } } - // [END spanner_query_data_with_timestamp_column] + // [END spanner_postgresql_query_data_with_timestamp_column] - // [START spanner_create_table_with_timestamp_column] + // [START spanner_postgresql_create_table_with_timestamp_column] static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, DatabaseId id) { OperationFuture op = dbAdminClient.updateDatabaseDdl( @@ -799,8 +803,8 @@ static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, Database "CREATE TABLE Performances (" + " SingerId BIGINT NOT NULL," + " VenueId BIGINT NOT NULL," - + " Revenue BIGINT, " - + " LastUpdateTime TIMESTAMPTZ NOT NULL," + + " Revenue BIGINT," + + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," + " PRIMARY KEY (SingerId, VenueId))" + " INTERLEAVE IN PARENT Singers ON DELETE CASCADE"), null); @@ -817,12 +821,12 @@ static void createTableWithTimestamp(DatabaseAdminClient dbAdminClient, Database throw SpannerExceptionFactory.propagateInterrupt(e); } } - // [END spanner_create_table_with_timestamp_column] + // [END spanner_postgresql_create_table_with_timestamp_column] - // [START spanner_insert_data_with_timestamp_column] + // [START spanner_postgresql_insert_data_with_timestamp_column] static void writeExampleDataWithTimestamp(DatabaseClient dbClient) { List mutations = new ArrayList<>(); - for (SpannerSample.Performance performance : PERFORMANCES) { + for (Performance performance : PERFORMANCES) { mutations.add( Mutation.newInsertBuilder("Performances") .set("SingerId") @@ -832,12 +836,12 @@ static void writeExampleDataWithTimestamp(DatabaseClient dbClient) { .set("Revenue") .to(performance.revenue) .set("LastUpdateTime") - .to(Timestamp.now()) + .to(Value.COMMIT_TIMESTAMP) .build()); } dbClient.write(mutations); } - // [END spanner_insert_data_with_timestamp_column] + // [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 @@ -864,33 +868,7 @@ static void queryPerformancesTable(DatabaseClient dbClient) { } } - // [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] - - // [START spanner_dml_standard_insert] + // [START spanner_postgresql_dml_standard_insert] static void insertUsingDml(DatabaseClient dbClient) { dbClient .readWriteTransaction() @@ -903,9 +881,9 @@ static void insertUsingDml(DatabaseClient dbClient) { return null; }); } - // [END spanner_dml_standard_insert] + // [END spanner_postgresql_dml_standard_insert] - // [START spanner_dml_standard_update] + // [START spanner_postgresql_dml_standard_update] static void updateUsingDml(DatabaseClient dbClient) { dbClient .readWriteTransaction() @@ -919,9 +897,9 @@ static void updateUsingDml(DatabaseClient dbClient) { return null; }); } - // [END spanner_dml_standard_update] + // [END spanner_postgresql_dml_standard_update] - // [START spanner_dml_standard_delete] + // [START spanner_postgresql_dml_standard_delete] static void deleteUsingDml(DatabaseClient dbClient) { dbClient .readWriteTransaction() @@ -932,9 +910,9 @@ static void deleteUsingDml(DatabaseClient dbClient) { return null; }); } - // [END spanner_dml_standard_delete] + // [END spanner_postgresql_dml_standard_delete] - // [START spanner_dml_write_then_read] + // [START spanner_postgresql_dml_write_then_read] static void writeAndReadUsingDml(DatabaseClient dbClient) { dbClient .readWriteTransaction() @@ -960,25 +938,25 @@ static void writeAndReadUsingDml(DatabaseClient dbClient) { return null; }); } - // [END spanner_dml_write_then_read] + // [END spanner_postgresql_dml_write_then_read] - // [START spanner_dml_partitioned_update] + // [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_dml_partitioned_update] + // [END spanner_postgresql_dml_partitioned_update] - // [START spanner_dml_partitioned_delete] + // [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_dml_partitioned_delete] + // [END spanner_postgresql_dml_partitioned_delete] - // [START spanner_dml_batch_update] + // [START spanner_postgresql_dml_batch_update] static void updateUsingBatchDml(DatabaseClient dbClient) { dbClient .readWriteTransaction() @@ -1006,9 +984,9 @@ static void updateUsingBatchDml(DatabaseClient dbClient) { return null; }); } - // [END spanner_dml_batch_update] + // [END spanner_postgresql_dml_batch_update] - // [START spanner_create_table_with_datatypes] + // [START spanner_postgresql_create_table_with_datatypes] static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, DatabaseId id) { OperationFuture op = dbAdminClient.updateDatabaseDdl( @@ -1023,7 +1001,7 @@ static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, Database + " OutdoorVenue BOOL, " + " PopularityScore FLOAT8, " + " Revenue NUMERIC, " - + " LastUpdateTime TIMESTAMPTZ NOT NULL," + + " LastUpdateTime SPANNER.COMMIT_TIMESTAMP NOT NULL," + " PRIMARY KEY (VenueId))"), null); try { @@ -1039,12 +1017,12 @@ static void createTableWithDatatypes(DatabaseAdminClient dbAdminClient, Database throw SpannerExceptionFactory.propagateInterrupt(e); } } - // [END spanner_create_table_with_datatypes] + // [END spanner_postgresql_create_table_with_datatypes] - // [START spanner_insert_datatypes_data] + // [START spanner_postgresql_insert_datatypes_data] static void writeDatatypesData(DatabaseClient dbClient) { List mutations = new ArrayList<>(); - for (PgSpannerSample.Venue venue : VENUES) { + for (Venue venue : VENUES) { mutations.add( Mutation.newInsertBuilder("Venues") .set("VenueId") @@ -1062,14 +1040,14 @@ static void writeDatatypesData(DatabaseClient dbClient) { .set("Revenue") .to(venue.revenue) .set("LastUpdateTime") - .to(Timestamp.now()) + .to(Value.COMMIT_TIMESTAMP) .build()); } dbClient.write(mutations); } - // [END spanner_insert_datatypes_data] + // [END spanner_postgresql_insert_datatypes_data] - // [START spanner_query_with_bool_parameter] + // [START spanner_postgresql_query_with_bool_parameter] static void queryWithBool(DatabaseClient dbClient) { boolean exampleBool = true; Statement statement = @@ -1090,16 +1068,16 @@ static void queryWithBool(DatabaseClient dbClient) { } } } - // [END spanner_query_with_bool_parameter] + // [END spanner_postgresql_query_with_bool_parameter] - // [START spanner_query_with_bytes_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") + "SELECT venueid as \"VenueId\", venuename as \"VenueName\" FROM Venues " + + "WHERE VenueInfo = $1") .bind("p1") .to(exampleBytes) .build(); @@ -1110,9 +1088,9 @@ static void queryWithBytes(DatabaseClient dbClient) { } } } - // [END spanner_query_with_bytes_parameter] + // [END spanner_postgresql_query_with_bytes_parameter] - // [START spanner_query_with_float_parameter] + // [START spanner_postgresql_query_with_float_parameter] static void queryWithFloat(DatabaseClient dbClient) { float exampleFloat = 0.8f; Statement statement = @@ -1133,9 +1111,9 @@ static void queryWithFloat(DatabaseClient dbClient) { } } } - // [END spanner_query_with_float_parameter] + // [END spanner_postgresql_query_with_float_parameter] - // [START spanner_query_with_int_parameter] + // [START spanner_postgresql_query_with_int_parameter] static void queryWithInt(DatabaseClient dbClient) { long exampleInt = 3000; Statement statement = @@ -1156,9 +1134,9 @@ static void queryWithInt(DatabaseClient dbClient) { } } } - // [END spanner_query_with_int_parameter] + // [END spanner_postgresql_query_with_int_parameter] - // [START spanner_query_with_string_parameter] + // [START spanner_postgresql_query_with_string_parameter] static void queryWithString(DatabaseClient dbClient) { String exampleString = "Venue 42"; Statement statement = @@ -1175,9 +1153,9 @@ static void queryWithString(DatabaseClient dbClient) { } } } - // [END spanner_query_with_string_parameter] + // [END spanner_postgresql_query_with_string_parameter] - // [START spanner_query_with_timestamp_parameter] + // [START spanner_postgresql_query_with_timestamp_parameter] static void queryWithTimestampParameter(DatabaseClient dbClient) { Statement statement = Statement.newBuilder( @@ -1197,9 +1175,9 @@ static void queryWithTimestampParameter(DatabaseClient dbClient) { } } } - // [END spanner_query_with_timestamp_parameter] + // [END spanner_postgresql_query_with_timestamp_parameter] - // [START spanner_query_with_numeric_parameter] + // [START spanner_postgresql_query_with_numeric_parameter] static void queryWithNumeric(DatabaseClient dbClient) { Statement statement = Statement.newBuilder( @@ -1219,9 +1197,9 @@ static void queryWithNumeric(DatabaseClient dbClient) { } } } - // [END spanner_query_with_numeric_parameter] + // [END spanner_postgresql_query_with_numeric_parameter] - // [START spanner_create_client_with_query_options] + // [START spanner_postgresql_create_client_with_query_options] static void clientWithQueryOptions(DatabaseId db) { SpannerOptions options = SpannerOptions.newBuilder() @@ -1230,7 +1208,7 @@ static void clientWithQueryOptions(DatabaseId db) { .newBuilder() .setOptimizerVersion("1") // The list of available statistics packages can be found by querying the - // "INFORMATION_SCHEMA.SPANNER_STATISTICS" table. + // "INFORMATION_SCHEMA.spanner_postgresql_STATISTICS" table. .setOptimizerStatisticsPackage("latest") .build()) .build(); @@ -1246,9 +1224,9 @@ static void clientWithQueryOptions(DatabaseId db) { } } } - // [END spanner_create_client_with_query_options] + // [END spanner_postgresql_create_client_with_query_options] - // [START spanner_query_with_query_options] + // [START spanner_postgresql_query_with_query_options] static void queryWithQueryOptions(DatabaseClient dbClient) { try (ResultSet resultSet = dbClient @@ -1260,7 +1238,7 @@ static void queryWithQueryOptions(DatabaseClient dbClient) { .newBuilder() .setOptimizerVersion("1") // The list of available statistics packages can be found by - // querying the "INFORMATION_SCHEMA.SPANNER_STATISTICS" table. + // querying the "INFORMATION_SCHEMA.spanner_postgresql_STATISTICS" table. .setOptimizerStatisticsPackage("latest") .build()) .build())) { @@ -1270,9 +1248,9 @@ static void queryWithQueryOptions(DatabaseClient dbClient) { } } } - // [END spanner_query_with_query_options] + // [END spanner_postgresql_query_with_query_options] - // [START spanner_list_backup_operations] + // [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. @@ -1303,9 +1281,9 @@ static void listBackupOperations(InstanceAdminClient instanceAdminClient, Databa } } } - // [END spanner_list_backup_operations] + // [END spanner_postgresql_list_backup_operations] - // [START spanner_list_database_operations] + // [START spanner_postgresql_list_database_operations] static void listDatabaseOperations( InstanceAdminClient instanceAdminClient, DatabaseAdminClient dbAdminClient, @@ -1332,7 +1310,7 @@ static void listDatabaseOperations( } } } - // [END spanner_list_database_operations] + // [END spanner_postgresql_list_database_operations] static void run( DatabaseClient dbClient, @@ -1341,8 +1319,8 @@ static void run( String command, DatabaseId database) { switch (command) { - case "createdatabase": - createDatabase(dbAdminClient, database); + case "createpgdatabase": + createPostgreSQLDatabase(dbAdminClient, database); break; case "write": writeExampleData(dbClient); @@ -1419,9 +1397,6 @@ static void run( case "queryperformancestable": queryPerformancesTable(dbClient); break; - case "writestructdata": - writeStructExampleData(dbClient); - break; case "insertusingdml": insertUsingDml(dbClient); break; @@ -1492,7 +1467,7 @@ static void printUsageAndExit() { System.err.println(" PgSpannerExample "); System.err.println(); System.err.println("Examples:"); - System.err.println(" PgSpannerExample createdatabase my-instance example-db"); + System.err.println(" PgSpannerExample createpgdatabase my-instance example-db"); System.err.println(" PgSpannerExample write my-instance example-db"); System.err.println(" PgSpannerExample delete my-instance example-db"); System.err.println(" PgSpannerExample query my-instance example-db"); diff --git a/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java b/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java index 8a0c9c2b82c..db5c7362cf8 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgAsyncExamplesIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google Inc. + * 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. diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java index 6a395b99171..796df092c80 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerSampleIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Google Inc. + * 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. @@ -130,7 +130,7 @@ public void testSample() throws Exception { assertThat(databaseId).isNotNull(); System.out.println("Create Database ..."); - String out = runSample("createdatabase"); + String out = runSample("createpgdatabase"); assertThat(out).contains("Created database"); assertThat(out).contains(dbId.getName()); @@ -197,14 +197,17 @@ public void testSample() throws Exception { 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"); diff --git a/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java b/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java index 43c9c949f19..035b7cdc36b 100644 --- a/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/PgSpannerStandaloneExamplesIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google Inc. + * 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. From 1c900f172360c49cbbd5b9c79d3de43d5f3d0156 Mon Sep 17 00:00:00 2001 From: Sivakumar-Searce Date: Tue, 19 Apr 2022 09:11:29 +0530 Subject: [PATCH 11/13] docs: pg samples checkstyle fixes --- .../main/java/com/example/spanner/PgSpannerSample.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java index 3679d88f423..9cc1d5d8497 100644 --- a/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java @@ -193,7 +193,7 @@ static class Venue { } // [START spanner_postgresql_create_database] - static void createPostgreSQLDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { + static void createPostgreSqlDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) { OperationFuture op = dbAdminClient.createDatabase( dbAdminClient.newDatabaseBuilder(id).setDialect(Dialect.POSTGRESQL).build(), Collections.emptyList()); @@ -1237,8 +1237,8 @@ static void queryWithQueryOptions(DatabaseClient dbClient) { .withQueryOptions(ExecuteSqlRequest.QueryOptions .newBuilder() .setOptimizerVersion("1") - // The list of available statistics packages can be found by - // querying the "INFORMATION_SCHEMA.spanner_postgresql_STATISTICS" table. + // The list of available statistics packages can be found by querying + // the "INFORMATION_SCHEMA.spanner_postgresql_STATISTICS" table. .setOptimizerStatisticsPackage("latest") .build()) .build())) { @@ -1320,7 +1320,7 @@ static void run( DatabaseId database) { switch (command) { case "createpgdatabase": - createPostgreSQLDatabase(dbAdminClient, database); + createPostgreSqlDatabase(dbAdminClient, database); break; case "write": writeExampleData(dbClient); From 98a82e7c50ff73181d77648ae50c32743a8854cf Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Thu, 21 Apr 2022 13:58:44 +0000 Subject: [PATCH 12/13] =?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 | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 00209a3ee31..ff693a85dc5 100644 --- a/README.md +++ b/README.md @@ -49,20 +49,20 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies ```Groovy -implementation platform('com.google.cloud:libraries-bom:25.0.0') +implementation platform('com.google.cloud:libraries-bom:25.1.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.21.2' +implementation 'com.google.cloud:google-cloud-spanner:6.23.2' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.21.2" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.23.2" ``` ## Authentication @@ -264,6 +264,11 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | Get Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java) | | List Databases Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/ListDatabasesSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/ListDatabasesSample.java) | | List Instance Configs Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java) | +| Pg Async Query To List Async Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java) | +| Pg Async Runner Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java) | +| Pg Async Transaction Manager Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java) | +| Pg Query With Numeric Parameter Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java) | +| Pg Spanner Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java) | | Query Information Schema Database Options Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/QueryInformationSchemaDatabaseOptionsSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/QueryInformationSchemaDatabaseOptionsSample.java) | | Query With Json Parameter Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/QueryWithJsonParameterSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/QueryWithJsonParameterSample.java) | | Query With Numeric Parameter Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/QueryWithNumericParameterSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/QueryWithNumericParameterSample.java) | From 64d87ab6460b63f06df2f5a1ace71aa55ea74d29 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 29 Apr 2022 16:27:27 +0000 Subject: [PATCH 13/13] =?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 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cd91bbd4e5e..ebef4886f96 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies ```Groovy -implementation platform('com.google.cloud:libraries-bom:25.1.0') +implementation platform('com.google.cloud:libraries-bom:25.2.0') implementation 'com.google.cloud:google-cloud-spanner' ``` @@ -265,10 +265,15 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | Get Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/GetInstanceConfigSample.java) | | List Databases Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/ListDatabasesSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/ListDatabasesSample.java) | | List Instance Configs Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/ListInstanceConfigsSample.java) | +| Pg Async Query To List Async Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgAsyncQueryToListAsyncExample.java) | +| Pg Async Runner Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgAsyncRunnerExample.java) | +| Pg Async Transaction Manager Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgAsyncTransactionManagerExample.java) | | Pg Batch Dml Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgBatchDmlSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgBatchDmlSample.java) | | Pg Case Sensitivity Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgCaseSensitivitySample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgCaseSensitivitySample.java) | | Pg Interleaved Table Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgInterleavedTableSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgInterleavedTableSample.java) | | Pg Partitioned Dml Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgPartitionedDmlSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgPartitionedDmlSample.java) | +| Pg Query With Numeric Parameter Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgQueryWithNumericParameterSample.java) | +| Pg Spanner Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/PgSpannerSample.java) | | Query Information Schema Database Options Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/QueryInformationSchemaDatabaseOptionsSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/QueryInformationSchemaDatabaseOptionsSample.java) | | Query With Json Parameter Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/QueryWithJsonParameterSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/QueryWithJsonParameterSample.java) | | Query With Numeric Parameter Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/QueryWithNumericParameterSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/QueryWithNumericParameterSample.java) |