From edc5bbf0d9d4faf48fd9a8d479d5bc5de938c82d Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 6 Feb 2023 13:29:38 +0530 Subject: [PATCH 01/11] fix: prevent illegal negative timeout values into thread sleep() method while retrying exceptions in unit tests. * For details on issue see - https://github.com/googleapis/java-spanner/issues/2206 --- .../com/google/cloud/spanner/it/ITClosedSessionTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java index aeb0256285b..227611a10de 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java @@ -251,7 +251,10 @@ public void testTransactionManager() throws InterruptedException { break; } } catch (AbortedException e) { - Thread.sleep(e.getRetryDelayInMillis()); + long retryDelayInMillis = e.getRetryDelayInMillis(); + if(retryDelayInMillis > 0) { + Thread.sleep(retryDelayInMillis); + } txn = manager.resetForRetry(); } } From 4cd497b05eab3e3b6b89b582bfafde80d42c1518 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Wed, 8 Feb 2023 15:27:18 +0530 Subject: [PATCH 02/11] Fixing lint issues. --- .../java/com/google/cloud/spanner/it/ITClosedSessionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java index 227611a10de..efbffcfa899 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITClosedSessionTest.java @@ -252,7 +252,7 @@ public void testTransactionManager() throws InterruptedException { } } catch (AbortedException e) { long retryDelayInMillis = e.getRetryDelayInMillis(); - if(retryDelayInMillis > 0) { + if (retryDelayInMillis > 0) { Thread.sleep(retryDelayInMillis); } txn = manager.resetForRetry(); From 6f97b2c1dbe882244c55aa512ccd76d21dd49fe4 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Thu, 16 Mar 2023 02:33:23 +0530 Subject: [PATCH 03/11] test: foreign key on delete cascade --- .../it/ITForeignKeyDeleteCascadeTest.java | 540 ++++++++++++++++++ 1 file changed, 540 insertions(+) create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java new file mode 100644 index 00000000000..c188a15e2a7 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java @@ -0,0 +1,540 @@ +package com.google.cloud.spanner.it; + +import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; + +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.ErrorCode; +import com.google.cloud.spanner.IntegrationTestEnv; +import com.google.cloud.spanner.Key; +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.ParallelIntegrationTest; +import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.testing.EmulatorSpannerHelper; +import com.google.cloud.spanner.testing.RemoteSpannerHelper; +import com.google.common.collect.ImmutableList; +import com.google.spanner.v1.BeginTransactionRequest; +import com.google.spanner.v1.CommitRequest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@Category(ParallelIntegrationTest.class) +@RunWith(Parameterized.class) +public class ITForeignKeyDeleteCascadeTest { + + @ClassRule + public static IntegrationTestEnv env = new IntegrationTestEnv(); + + @Parameterized.Parameters(name = "Dialect = {0}") + public static List data() { + List params = new ArrayList<>(); + params.add(new DialectTestParameter(Dialect.GOOGLE_STANDARD_SQL)); + params.add(new DialectTestParameter(Dialect.POSTGRESQL)); + return params; + } + + private static final String TABLE_NAME_SINGER = "Singer"; + private static final String TABLE_NAME_CONCERTS = "Concerts"; + private static final String CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME = "Fk_Concerts_Singer"; + private static final String CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 = "Fk_Concerts_Singer_V2"; + private static final String DELETE_RULE_CASCADE = "CASCADE"; + private static final String DELETE_RULE_DEFAULT = "NO ACTION"; + private static final String DELETE_RULE_COLUMN_NAME = "DELETE_RULE"; + private static final String CREATE_TABLE_SINGER = "CREATE TABLE Singer (\n" + + " SingerId INT64 NOT NULL,\n" + + " FirstName STRING(1024),\n" + + ") PRIMARY KEY(SingerId)\n"; + + private static final String POSTGRES_CREATE_TABLE_SINGER = "CREATE TABLE Singer (\n" + + " singer_id BIGINT PRIMARY KEY,\n" + + " first_name VARCHAR\n" + + ")"; + + private static final String CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY = "CREATE TABLE Concerts (\n" + + " VenueId INT64 NOT NULL,\n" + + " SingerId INT64 NOT NULL,\n" + + " CONSTRAINT Fk_Concerts_Singer FOREIGN KEY (SingerId) REFERENCES Singer (SingerId) ON DELETE CASCADE" + + ") PRIMARY KEY(VenueId, SingerId)"; + + private static final String POSTGRES_CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY = + "CREATE TABLE Concerts (\n" + + " venue_id BIGINT NOT NULL,\n" + + " singer_id BIGINT NOT NULL,\n" + + " PRIMARY KEY (venue_id, singer_id),\n" + + " CONSTRAINT Fk_Concerts_Singer FOREIGN KEY (singer_id) REFERENCES Singer (singer_id) ON DELETE CASCADE\n" + + " )"; + + private static final String CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY = "CREATE TABLE ConcertsV2 (\n" + + " VenueId INT64 NOT NULL,\n" + + " SingerId INT64 NOT NULL,\n" + + ") PRIMARY KEY(VenueId, SingerId)"; + + private static final String POSTGRES_CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY = + "CREATE TABLE ConcertsV2 (\n" + + " venue_id BIGINT NOT NULL,\n" + + " singer_id BIGINT NOT NULL,\n" + + " PRIMARY KEY (venue_id, singer_id)\n" + + " )"; + + private static final String ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY = "ALTER TABLE ConcertsV2 " + + "ADD CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 + + " FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) " + + "ON DELETE CASCADE"; + + private static final String POSTGRES_ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY = + "ALTER TABLE ConcertsV2 " + + "ADD CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 + + " FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) " + + "ON DELETE CASCADE"; + + private static final String ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE = + "ALTER TABLE ConcertsV2 " + + "ADD CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 + + " FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) "; + + private static final String POSTGRES_ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE = + "ALTER TABLE ConcertsV2 " + + "ADD CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 + + " FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) "; + + private static final String ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT = + "ALTER TABLE ConcertsV2\n" + + "DROP CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2; + + private static final String QUERY_REFERENTIAL_CONSTRAINTS = "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME =" + "\"" + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME + + "\""; + private static final String POSTGRES_QUERY_REFERENTIAL_CONSTRAINTS = "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME =" + "'" + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME + + "'"; + + private static final String QUERY_REFERENTIAL_CONSTRAINTS_V2 = "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME =" + "\"" + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 + + "\""; + private static final String POSTGRES_QUERY_REFERENTIAL_CONSTRAINTS_V2 = "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME =" + "'" + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 + + "'"; + + private static Database GOOGLE_STANDARD_SQL_DATABASE; + private static Database POST_GRE_SQL_DATABASE; + private static List dbs = new ArrayList<>(); + + + @Parameterized.Parameter(0) + public DialectTestParameter dialect; + + @BeforeClass + public static void setUpDatabase() { + GOOGLE_STANDARD_SQL_DATABASE = + env.getTestHelper() + .createTestDatabase(ImmutableList.of( + CREATE_TABLE_SINGER, CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); + if (!isUsingEmulator()) { + POST_GRE_SQL_DATABASE = + env.getTestHelper() + .createTestDatabase( + Dialect.POSTGRESQL, + ImmutableList.of(POSTGRES_CREATE_TABLE_SINGER, + POSTGRES_CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); + } + dbs.add(GOOGLE_STANDARD_SQL_DATABASE); + dbs.add(POST_GRE_SQL_DATABASE); + } + + @AfterClass + public static void tearDown() { + for (Database db : dbs) { + db.drop(); + } + dbs.clear(); + } + + @Test + public void testForeignKeyDeleteCascadeConstraints_withCreateDDLStatements() { + final DatabaseClient databaseClient = getCreatedDatabaseClient(); + final String referentialConstraintQuery = getReferentialConstraintsQueryStatement(); + try (final ResultSet rs = + databaseClient + .singleUse() + .executeQuery(Statement.of(referentialConstraintQuery))) { + while (rs.next()) { + assertThat(rs.getString(DELETE_RULE_COLUMN_NAME)).isEqualTo(DELETE_RULE_CASCADE); + } + } + } + + @Test + public void testForeignKeyDeleteCascadeConstraints_withAlterDDLStatements() throws Exception { + // Creating new tables within this test to ensure we don't pollute tables used by other tests in this class. + final List createStatements = getCreateAndAlterTableStatementsWithForeignKey(); + final Database createdDatabase = createDatabase(createStatements); + dbs.add(createdDatabase); + + final DatabaseClient databaseClient = getCreatedDatabaseClient(); + + final String referentialConstraintQuery = getReferentialConstraintsQueryStatement(); + try (final ResultSet rs = + databaseClient + .singleUse() + .executeQuery( + Statement.of(referentialConstraintQuery))) { + while (rs.next()) { + assertThat(rs.getString(DELETE_RULE_COLUMN_NAME)).isEqualTo(DELETE_RULE_CASCADE); + } + } + + // remove the foreign key delete cascade constraint + final List alterDropStatements = getAlterDropForeignKeyDeleteCascadeStatements(); + getDatabaseAdminClient() + .updateDatabaseDdl(env.getTestHelper().getInstanceId().getInstance(), + createdDatabase.getId().getDatabase(), alterDropStatements, null) + .get(); + + try (final ResultSet rs = + databaseClient + .singleUse() + .executeQuery(Statement.of(getReferentialConstraintsQueryStatementV2()))) { + while (rs.next()) { + assertThat(rs.getString(DELETE_RULE_COLUMN_NAME)).isEqualTo(DELETE_RULE_DEFAULT); + } + } + } + + @Test + public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { + + final DatabaseClient databaseClient = getCreatedDatabaseClient(); + final String singerInsertStatement = getInsertStatementForSingerTable(); + final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1").to(1L) + .bind("p2").to("singerName").build(); + + final String concertInsertStatement = getInsertStatementForConcertsTable(); + final Statement concertInsertStatementWithValues = Statement.newBuilder(concertInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1").to(1L) + .bind("p2").to(1L).build(); + + // successful inserts into referenced and referencing tables + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(singerInsertStatementWithValues); + return null; + }); + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(concertInsertStatementWithValues); + return null; + }); + + final String singerIdColumnName = getSingerIdColumnName(); + final String singerFirstNameColumnName = getSingerFirstNameColumnName(); + final String concertVenueIdColumnName = getConcertVenueIdColumnName(); + + try (ResultSet resultSet = + databaseClient.singleUse() + .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_SINGER))) { + + resultSet.next(); + assertEquals(1, resultSet.getLong(singerIdColumnName)); + assertEquals("singerName", resultSet.getString(singerFirstNameColumnName)); + + assertThat(resultSet.next()).isFalse(); + } + + try (ResultSet resultSet = + databaseClient.singleUse() + .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_CONCERTS))) { + + resultSet.next(); + assertEquals(1, resultSet.getLong(singerIdColumnName)); + assertEquals(1, resultSet.getLong(concertVenueIdColumnName)); + + assertThat(resultSet.next()).isFalse(); + } + } + + @Test + public void testForeignKeyDeleteCascadeConstraints_verifyInvalidInsertions() { + final DatabaseClient databaseClient = getCreatedDatabaseClient(); + + // unsuccessful inserts into referencing tables when foreign key is not inserted into referenced table + final String concertInsertStatement = getInsertStatementForConcertsTable(); + final Statement concertInsertStatementWithInvalidValues = Statement.newBuilder( + concertInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1").to(2L) + .bind("p2").to(2L).build(); + try { + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(concertInsertStatementWithInvalidValues); + return null; + }); + fail("Expected exception"); + } catch (SpannerException ex) { + assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(ex.getMessage()).contains("Cannot find referenced values"); + } + } + + @Test + public void testForeignKeyDeleteCascadeConstraints_forDeletions() { + + final DatabaseClient databaseClient = getCreatedDatabaseClient(); + + final String singerInsertStatement = getInsertStatementForSingerTable(); + final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1").to(3L) + .bind("p2").to("singerName").build(); + + final String concertInsertStatement = getInsertStatementForConcertsTable(); + final Statement concertInsertStatementWithValues = Statement.newBuilder(concertInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1").to(3L) + .bind("p2").to(3L).build(); + + // successful inserts into referenced and referencing tables + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(singerInsertStatementWithValues); + return null; + }); + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(concertInsertStatementWithValues); + return null; + }); + + // execute delete + final String singerDeleteStatement = getDeleteStatementForSingerTable(); + final Statement singerDeleteStatementWithValues = Statement.newBuilder(singerDeleteStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1").to(3L).build(); + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(singerDeleteStatementWithValues); + return null; + }); + + try (ResultSet resultSet = + databaseClient.singleUse() + .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_SINGER))) { + assertThat(resultSet.next()).isFalse(); + } + + try (ResultSet resultSet = + databaseClient.singleUse() + .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_CONCERTS))) { + assertThat(resultSet.next()).isFalse(); + } + } + + @Test + public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictDueToParentTable() { + final DatabaseClient databaseClient = getCreatedDatabaseClient(); + + // inserting and deleting the referenced key within the same mutation are considered + // conflicting operations, thus this results in an exception. + try { + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.buffer( + Arrays.asList( + Mutation.newInsertBuilder("Singer") + .set(getSingerIdColumnName()).to(4L) + .set(getSingerFirstNameColumnName()).to("singerName").build(), + Mutation.delete("Singer", Key.of(4L)))); + return null; + }); + fail("Expected exception"); + } catch (SpannerException ex) { + assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + } + } + + @Test + public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictsDueToChildTable() { + final DatabaseClient databaseClient = getCreatedDatabaseClient(); + + // referencing a foreign key in child table and deleting the referenced key in parent table + // within the same mutations are considered conflicting operations. + try { + final String singerInsertStatement = getInsertStatementForSingerTable(); + final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1").to(5L) + .bind("p2").to("singerName").build(); + + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(singerInsertStatementWithValues); + return null; + }); + + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.buffer( + Arrays.asList( + Mutation.newInsertBuilder("Concerts") + .set(getConcertVenueIdColumnName()).to(5L) + .set(getSingerIdColumnName()).to(5L).build(), + Mutation.delete("Singer", Key.of(5L)))); + return null; + }); + fail("Expected exception"); + } catch (SpannerException ex) { + assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + } + } + + private Database createDatabase(final List statements) { + final Database database; + if (dialect.dialect == Dialect.POSTGRESQL) { + database = env.getTestHelper() + .createTestDatabase(Dialect.POSTGRESQL, statements); + } else { + database = env.getTestHelper() + .createTestDatabase(statements); + } + return database; + } + + private String getReferentialConstraintsQueryStatement() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return POSTGRES_QUERY_REFERENTIAL_CONSTRAINTS; + } else { + return QUERY_REFERENTIAL_CONSTRAINTS; + } + } + + private String getReferentialConstraintsQueryStatementV2() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return POSTGRES_QUERY_REFERENTIAL_CONSTRAINTS_V2; + } else { + return QUERY_REFERENTIAL_CONSTRAINTS_V2; + } + } + + private List getCreateAndAlterTableStatementsWithForeignKey() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return ImmutableList.of(POSTGRES_CREATE_TABLE_SINGER, + POSTGRES_CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY, + POSTGRES_ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY); + } else { + return ImmutableList.of(CREATE_TABLE_SINGER, CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY, + ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY); + } + } + + private List getAlterDropForeignKeyDeleteCascadeStatements() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return ImmutableList.of(ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT, + POSTGRES_ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE); + } else { + return ImmutableList.of(ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT, + ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE); + } + } + + private DatabaseAdminClient getDatabaseAdminClient() { + return env.getTestHelper().getClient().getDatabaseAdminClient(); + } + private DatabaseClient getCreatedDatabaseClient() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return env.getTestHelper().getDatabaseClient(this.POST_GRE_SQL_DATABASE); + } + return env.getTestHelper().getDatabaseClient(this.GOOGLE_STANDARD_SQL_DATABASE); + } + + private String getInsertStatementForSingerTable() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return "INSERT INTO Singer (singer_id, first_name) VALUES ($1, $2)"; + } else { + return "INSERT INTO Singer (SingerId, FirstName) VALUES (@p1, @p2)"; + } + } + + private String getInsertStatementForConcertsTable() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return "INSERT INTO Concerts (venue_id, singer_id) VALUES ($1, $2)"; + } else { + return "INSERT INTO Concerts (VenueId, SingerId) VALUES (@p1, @p2)"; + } + } + + private String getDeleteStatementForSingerTable() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return "DELETE FROM Singer WHERE singer_id = $1"; + } else { + return "DELETE FROM Singer WHERE SingerId = @p1"; + } + } + + private String getConcertVenueIdColumnName() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return "venue_id"; + } else { + return "VenueId"; + } + } + + private String getSingerFirstNameColumnName() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return "first_name"; + } else { + return "FirstName"; + } + } + + private String getSingerIdColumnName() { + if (dialect.dialect == Dialect.POSTGRESQL) { + return "singer_id"; + } else { + return "SingerId"; + } + } +} From b492fe936c2c0d99221cb0312f2d6119da3f9966 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 20 Mar 2023 19:57:13 +0530 Subject: [PATCH 04/11] docs:adding samples for foreign key on delete cascade feature. --- ...ableWithForeignKeyDeleteCascadeSample.java | 66 +++++++++++++++++ ...ableWithForeignKeyDeleteCascadeSample.java | 70 +++++++++++++++++++ ...reignKeyConstraintDeleteCascadeSample.java | 62 ++++++++++++++++ ...leWithForeignKeyDeleteCascadeSampleIT.java | 41 +++++++++++ ...leWithForeignKeyDeleteCascadeSampleIT.java | 31 ++++++++ ...ignKeyConstraintDeleteCascadeSampleIT.java | 40 +++++++++++ .../com/example/spanner/SampleTestBase.java | 10 ++- 7 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java create mode 100644 samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java create mode 100644 samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java diff --git a/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java new file mode 100644 index 00000000000..522600cf350 --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java @@ -0,0 +1,66 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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_alter_table_with_foreign_key_delete_cascade] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceConfigId; +import com.google.cloud.spanner.InstanceId; +import com.google.cloud.spanner.InstanceInfo; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.common.collect.ImmutableList; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; +import java.util.concurrent.ExecutionException; + +class AlterTableWithForeignKeyDeleteCascadeSample { + + static void alterForeignKeyDeleteCascadeConstraint() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + alterForeignKeyDeleteCascadeConstraint(adminClient, instanceId, databaseId); + } + } + + static void alterForeignKeyDeleteCascadeConstraint( + DatabaseAdminClient adminClient, String instanceId, String databaseId) { + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of("ALTER TABLE ShoppingCarts\n" + + " ADD CONSTRAINT FKShoppingCartsCustomerName\n" + + " FOREIGN KEY (CustomerName)\n" + + " REFERENCES Customers(CustomerName)\n" + + " ON DELETE CASCADE\n"), + null); + + System.out.printf(String.format("Altered ShoppingCarts table with FKShoppingCartsCustomerName\n" + + "foreign key constraint on database %s on instance %s", databaseId, instanceId)); + + } +} +// [END spanner_alter_table_with_foreign_key_delete_cascade] diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java new file mode 100644 index 00000000000..0e21865df7c --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java @@ -0,0 +1,70 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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_create_table_with_foreign_key_delete_cascade] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceConfigId; +import com.google.cloud.spanner.InstanceId; +import com.google.cloud.spanner.InstanceInfo; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.common.collect.ImmutableList; +import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; +import java.util.concurrent.ExecutionException; + +class CreateTableWithForeignKeyDeleteCascadeSample { + + static void createForeignKeyDeleteCascadeConstraint() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + createForeignKeyDeleteCascadeConstraint(adminClient, instanceId, databaseId); + } + } + + static void createForeignKeyDeleteCascadeConstraint( + DatabaseAdminClient adminClient, String instanceId, String databaseId) { + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of("CREATE TABLE Customers (\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " ) PRIMARY KEY (CustomerId)", + "CREATE TABLE ShoppingCarts (\n" + + " CartId INT64 NOT NULL,\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId)\n" + + " REFERENCES Customers (CustomerId) ON DELETE CASCADE\n" + + " ) PRIMARY KEY (CartId)\n"), + null); + + System.out.printf(String.format("Created Customers and ShoppingCarts table with FKShoppingCartsCustomerId\n" + + "foreign key constraint on database %s on instance %s\n", databaseId, instanceId)); + } +} +// [END spanner_create_table_with_foreign_key_delete_cascade] diff --git a/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java new file mode 100644 index 00000000000..aff0b624e1d --- /dev/null +++ b/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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_drop_foreign_key_constraint_delete_cascade] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.InstanceAdminClient; +import com.google.cloud.spanner.InstanceConfigId; +import com.google.cloud.spanner.InstanceId; +import com.google.cloud.spanner.InstanceInfo; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.common.collect.ImmutableList; +import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; +import java.util.concurrent.ExecutionException; + +class DropForeignKeyConstraintDeleteCascadeSample { + + static void deleteForeignKeyDeleteCascadeConstraint() { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + + try (Spanner spanner = + SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) { + DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient(); + deleteForeignKeyDeleteCascadeConstraint(adminClient, instanceId, databaseId); + } + } + + static void deleteForeignKeyDeleteCascadeConstraint( + DatabaseAdminClient adminClient, String instanceId, String databaseId) { + adminClient.updateDatabaseDdl( + instanceId, + databaseId, + ImmutableList.of("ALTER TABLE ShoppingCarts\n" + + " DROP CONSTRAINT FKShoppingCartsCustomerName\n"), + null); + + System.out.printf(String.format("Altered ShoppingCarts table to drop FKShoppingCartsCustomerName\n" + + "foreign key constraint on database %s on instance %s\n", databaseId, instanceId)); + + } +} +// [END spanner_drop_foreign_key_constraint_delete_cascade] \ No newline at end of file diff --git a/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java new file mode 100644 index 00000000000..4f32b6dff9f --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java @@ -0,0 +1,41 @@ +package com.example.spanner; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import org.junit.Test; + +public class AlterTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBase { + + @Test + public void testAlterTableWithForeignKeyDeleteCascade() throws Exception { + + // Creates database + final String databaseId = idGenerator.generateDatabaseId(); + databaseAdminClient.createDatabase(instanceId, databaseId, Arrays.asList("CREATE TABLE Customers (\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " ) PRIMARY KEY (CustomerId)", + "CREATE TABLE ShoppingCarts (\n" + + " CartId INT64 NOT NULL,\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId)\n" + + " REFERENCES Customers (CustomerId) ON DELETE CASCADE\n" + + " ) PRIMARY KEY (CartId)\n") + ).get(5, TimeUnit.MINUTES); + + // Runs sample + final String out = SampleRunner.runSample(() -> AlterTableWithForeignKeyDeleteCascadeSample + .alterForeignKeyDeleteCascadeConstraint(databaseAdminClient, instanceId, databaseId) + ); + + assertTrue( + "Expected to have created database " + databaseId + " with tables containing " + + "foreign key constraints.", out.contains("Altered ShoppingCarts table " + + "with FKShoppingCartsCustomerName" + ) + ); + } +} \ No newline at end of file diff --git a/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java new file mode 100644 index 00000000000..dbe768cf81b --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java @@ -0,0 +1,31 @@ +package com.example.spanner; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import org.junit.Test; + +public class CreateTableWithForeignKeyDeleteCascadeSampleIT extends SampleTestBase { + + @Test + public void testCreateTableWithForeignKeyDeleteCascade() throws Exception { + + // Creates database + final String databaseId = idGenerator.generateDatabaseId(); + databaseAdminClient.createDatabase(instanceId, databaseId, Arrays.asList() + ).get(5, TimeUnit.MINUTES); + + // Runs sample + final String out = SampleRunner.runSample(() -> CreateTableWithForeignKeyDeleteCascadeSample + .createForeignKeyDeleteCascadeConstraint(databaseAdminClient, instanceId, databaseId) + ); + + assertTrue( + "Expected to have created database " + databaseId + " with tables containing " + + "foreign key constraints.", out.contains("Created Customers and ShoppingCarts table " + + "with FKShoppingCartsCustomerId" + ) + ); + } +} \ No newline at end of file diff --git a/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java new file mode 100644 index 00000000000..8e02d74c761 --- /dev/null +++ b/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java @@ -0,0 +1,40 @@ +package com.example.spanner; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import org.junit.Test; + +public class DropForeignKeyConstraintDeleteCascadeSampleIT extends SampleTestBase { + + @Test + public void testDropForeignKeyConstraintDeleteCascade() throws Exception { + + // Creates database + final String databaseId = idGenerator.generateDatabaseId(); + databaseAdminClient.createDatabase(instanceId, databaseId, Arrays.asList("CREATE TABLE Customers (\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " ) PRIMARY KEY (CustomerId)", + "CREATE TABLE ShoppingCarts (\n" + + " CartId INT64 NOT NULL,\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " CONSTRAINT FKShoppingCartsCustomerName FOREIGN KEY (CustomerName)\n" + + " REFERENCES Customers (CustomerName) ON DELETE CASCADE\n" + + " ) PRIMARY KEY (CartId)\n") + ).get(5, TimeUnit.MINUTES); + + // Runs sample + final String out = SampleRunner.runSample(() -> DropForeignKeyConstraintDeleteCascadeSample + .deleteForeignKeyDeleteCascadeConstraint(databaseAdminClient, instanceId, databaseId) + ); + + assertTrue( + "Expected to have dropped foreign-key constraints from tables in created database " + + databaseId , out.contains("Altered ShoppingCarts table to drop FKShoppingCartsCustomerName" + ) + ); + } +} \ No newline at end of file diff --git a/samples/snippets/src/test/java/com/example/spanner/SampleTestBase.java b/samples/snippets/src/test/java/com/example/spanner/SampleTestBase.java index 47c6f4f0230..39424c8d080 100644 --- a/samples/snippets/src/test/java/com/example/spanner/SampleTestBase.java +++ b/samples/snippets/src/test/java/com/example/spanner/SampleTestBase.java @@ -44,10 +44,14 @@ public class SampleTestBase { @BeforeClass public static void beforeClass() { - final SpannerOptions options = SpannerOptions + final String serverUrl = ""; + final SpannerOptions.Builder optionsBuilder = SpannerOptions .newBuilder() - .setAutoThrottleAdministrativeRequests() - .build(); + .setAutoThrottleAdministrativeRequests(); + if (!serverUrl.isEmpty()) { + optionsBuilder.setHost(serverUrl); + } + final SpannerOptions options = optionsBuilder.build(); projectId = options.getProjectId(); spanner = options.getService(); databaseAdminClient = spanner.getDatabaseAdminClient(); From a1b801a8d8715cc037ffaca0b7ece591125a5cab Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 27 Mar 2023 12:29:11 +0530 Subject: [PATCH 05/11] incorporated review comments. --- .../it/ITForeignKeyDeleteCascadeTest.java | 240 ++++++++---------- ...ableWithForeignKeyDeleteCascadeSample.java | 29 +-- ...ableWithForeignKeyDeleteCascadeSample.java | 26 +- ...reignKeyConstraintDeleteCascadeSample.java | 25 +- ...leWithForeignKeyDeleteCascadeSampleIT.java | 65 +++-- ...ignKeyConstraintDeleteCascadeSampleIT.java | 60 +++-- 6 files changed, 218 insertions(+), 227 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java index c188a15e2a7..886eaa389e5 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java @@ -1,10 +1,24 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.google.cloud.spanner.it; import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; +import static org.junit.Assert.assertThrows; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseAdminClient; @@ -18,18 +32,11 @@ import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.Statement; -import com.google.cloud.spanner.testing.EmulatorSpannerHelper; -import com.google.cloud.spanner.testing.RemoteSpannerHelper; import com.google.common.collect.ImmutableList; -import com.google.spanner.v1.BeginTransactionRequest; -import com.google.spanner.v1.CommitRequest; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; -import org.junit.After; import org.junit.AfterClass; -import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -54,8 +61,6 @@ public static List data() { private static final String TABLE_NAME_SINGER = "Singer"; private static final String TABLE_NAME_CONCERTS = "Concerts"; - private static final String CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME = "Fk_Concerts_Singer"; - private static final String CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 = "Fk_Concerts_Singer_V2"; private static final String DELETE_RULE_CASCADE = "CASCADE"; private static final String DELETE_RULE_DEFAULT = "NO ACTION"; private static final String DELETE_RULE_COLUMN_NAME = "DELETE_RULE"; @@ -83,10 +88,11 @@ public static List data() { + " CONSTRAINT Fk_Concerts_Singer FOREIGN KEY (singer_id) REFERENCES Singer (singer_id) ON DELETE CASCADE\n" + " )"; - private static final String CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY = "CREATE TABLE ConcertsV2 (\n" - + " VenueId INT64 NOT NULL,\n" - + " SingerId INT64 NOT NULL,\n" - + ") PRIMARY KEY(VenueId, SingerId)"; + private static final String CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY = + "CREATE TABLE ConcertsV2 (\n" + + " VenueId INT64 NOT NULL,\n" + + " SingerId INT64 NOT NULL,\n" + + ") PRIMARY KEY(VenueId, SingerId)"; private static final String POSTGRES_CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY = "CREATE TABLE ConcertsV2 (\n" @@ -96,50 +102,28 @@ public static List data() { + " )"; private static final String ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY = "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 - + " FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) " + + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) " + "ON DELETE CASCADE"; private static final String POSTGRES_ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY = "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 - + " FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) " + + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) " + "ON DELETE CASCADE"; private static final String ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE = "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 - + " FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) "; + + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) "; private static final String POSTGRES_ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE = "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 - + " FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) "; + + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) "; private static final String ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT = "ALTER TABLE ConcertsV2\n" - + "DROP CONSTRAINT " + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2; - - private static final String QUERY_REFERENTIAL_CONSTRAINTS = "SELECT DELETE_RULE\n" - + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" - + "WHERE CONSTRAINT_NAME =" + "\"" + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME - + "\""; - private static final String POSTGRES_QUERY_REFERENTIAL_CONSTRAINTS = "SELECT DELETE_RULE\n" - + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" - + "WHERE CONSTRAINT_NAME =" + "'" + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME - + "'"; - - private static final String QUERY_REFERENTIAL_CONSTRAINTS_V2 = "SELECT DELETE_RULE\n" - + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" - + "WHERE CONSTRAINT_NAME =" + "\"" + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 - + "\""; - private static final String POSTGRES_QUERY_REFERENTIAL_CONSTRAINTS_V2 = "SELECT DELETE_RULE\n" - + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" - + "WHERE CONSTRAINT_NAME =" + "'" + CONCERTS_SINGER_FOREIGN_KEY_CONSTRAINT_NAME_V2 - + "'"; + + "DROP CONSTRAINT Fk_Concerts_Singer_V2"; private static Database GOOGLE_STANDARD_SQL_DATABASE; - private static Database POST_GRE_SQL_DATABASE; + private static Database POSTGRESQL_DATABASE; private static List dbs = new ArrayList<>(); @@ -153,7 +137,7 @@ public static void setUpDatabase() { .createTestDatabase(ImmutableList.of( CREATE_TABLE_SINGER, CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); if (!isUsingEmulator()) { - POST_GRE_SQL_DATABASE = + POSTGRESQL_DATABASE = env.getTestHelper() .createTestDatabase( Dialect.POSTGRESQL, @@ -161,7 +145,7 @@ public static void setUpDatabase() { POSTGRES_CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); } dbs.add(GOOGLE_STANDARD_SQL_DATABASE); - dbs.add(POST_GRE_SQL_DATABASE); + dbs.add(POSTGRESQL_DATABASE); } @AfterClass @@ -190,7 +174,8 @@ public void testForeignKeyDeleteCascadeConstraints_withCreateDDLStatements() { public void testForeignKeyDeleteCascadeConstraints_withAlterDDLStatements() throws Exception { // Creating new tables within this test to ensure we don't pollute tables used by other tests in this class. final List createStatements = getCreateAndAlterTableStatementsWithForeignKey(); - final Database createdDatabase = createDatabase(createStatements); + final Database createdDatabase = + env.getTestHelper().createTestDatabase(dialect.dialect, createStatements); dbs.add(createdDatabase); final DatabaseClient databaseClient = getCreatedDatabaseClient(); @@ -244,14 +229,9 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { .readWriteTransaction() .run( transaction -> { - transaction.executeUpdate(singerInsertStatementWithValues); - return null; - }); - databaseClient - .readWriteTransaction() - .run( - transaction -> { - transaction.executeUpdate(concertInsertStatementWithValues); + transaction.batchUpdate( + ImmutableList.of(singerInsertStatementWithValues, + concertInsertStatementWithValues)); return null; }); @@ -293,19 +273,17 @@ public void testForeignKeyDeleteCascadeConstraints_verifyInvalidInsertions() { // Use 'p1' to bind to the parameter with index 1 etc. .bind("p1").to(2L) .bind("p2").to(2L).build(); - try { - databaseClient - .readWriteTransaction() - .run( - transaction -> { - transaction.executeUpdate(concertInsertStatementWithInvalidValues); - return null; - }); - fail("Expected exception"); - } catch (SpannerException ex) { - assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(ex.getMessage()).contains("Cannot find referenced values"); - } + + SpannerException ex = assertThrows(SpannerException.class, () -> + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(concertInsertStatementWithInvalidValues); + return null; + })); + assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertThat(ex.getMessage()).contains("Cannot find referenced values"); } @Test @@ -330,14 +308,9 @@ public void testForeignKeyDeleteCascadeConstraints_forDeletions() { .readWriteTransaction() .run( transaction -> { - transaction.executeUpdate(singerInsertStatementWithValues); - return null; - }); - databaseClient - .readWriteTransaction() - .run( - transaction -> { - transaction.executeUpdate(concertInsertStatementWithValues); + transaction.batchUpdate( + ImmutableList.of(singerInsertStatementWithValues, + concertInsertStatementWithValues)); return null; }); @@ -373,23 +346,20 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictDueToP // inserting and deleting the referenced key within the same mutation are considered // conflicting operations, thus this results in an exception. - try { - databaseClient - .readWriteTransaction() - .run( - transaction -> { - transaction.buffer( - Arrays.asList( - Mutation.newInsertBuilder("Singer") - .set(getSingerIdColumnName()).to(4L) - .set(getSingerFirstNameColumnName()).to("singerName").build(), - Mutation.delete("Singer", Key.of(4L)))); - return null; - }); - fail("Expected exception"); - } catch (SpannerException ex) { - assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - } + SpannerException ex = assertThrows(SpannerException.class, () -> + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.buffer( + Arrays.asList( + Mutation.newInsertBuilder("Singer") + .set(getSingerIdColumnName()).to(4L) + .set(getSingerFirstNameColumnName()).to("singerName").build(), + Mutation.delete("Singer", Key.of(4L)))); + return null; + })); + assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); } @Test @@ -398,64 +368,55 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictsDueTo // referencing a foreign key in child table and deleting the referenced key in parent table // within the same mutations are considered conflicting operations. - try { - final String singerInsertStatement = getInsertStatementForSingerTable(); - final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) - // Use 'p1' to bind to the parameter with index 1 etc. - .bind("p1").to(5L) - .bind("p2").to("singerName").build(); - - databaseClient - .readWriteTransaction() - .run( - transaction -> { - transaction.executeUpdate(singerInsertStatementWithValues); - return null; - }); - - databaseClient - .readWriteTransaction() - .run( - transaction -> { - transaction.buffer( - Arrays.asList( - Mutation.newInsertBuilder("Concerts") - .set(getConcertVenueIdColumnName()).to(5L) - .set(getSingerIdColumnName()).to(5L).build(), - Mutation.delete("Singer", Key.of(5L)))); - return null; - }); - fail("Expected exception"); - } catch (SpannerException ex) { - assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - } - } + final String singerInsertStatement = getInsertStatementForSingerTable(); + final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1").to(5L) + .bind("p2").to("singerName").build(); - private Database createDatabase(final List statements) { - final Database database; - if (dialect.dialect == Dialect.POSTGRESQL) { - database = env.getTestHelper() - .createTestDatabase(Dialect.POSTGRESQL, statements); - } else { - database = env.getTestHelper() - .createTestDatabase(statements); - } - return database; + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(singerInsertStatementWithValues); + return null; + }); + SpannerException ex = assertThrows(SpannerException.class, () -> + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.buffer( + Arrays.asList( + Mutation.newInsertBuilder("Concerts") + .set(getConcertVenueIdColumnName()).to(5L) + .set(getSingerIdColumnName()).to(5L).build(), + Mutation.delete("Singer", Key.of(5L)))); + return null; + })); } private String getReferentialConstraintsQueryStatement() { if (dialect.dialect == Dialect.POSTGRESQL) { - return POSTGRES_QUERY_REFERENTIAL_CONSTRAINTS; + return "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME ='Fk_Concerts_Singer'"; } else { - return QUERY_REFERENTIAL_CONSTRAINTS; + return "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME =\"Fk_Concerts_Singer\""; } } private String getReferentialConstraintsQueryStatementV2() { if (dialect.dialect == Dialect.POSTGRESQL) { - return POSTGRES_QUERY_REFERENTIAL_CONSTRAINTS_V2; + return "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME ='Fk_Concerts_Singer_V2'"; } else { - return QUERY_REFERENTIAL_CONSTRAINTS_V2; + return "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME =\"Fk_Concerts_Singer_V2\""; } } @@ -483,9 +444,10 @@ private List getAlterDropForeignKeyDeleteCascadeStatements() { private DatabaseAdminClient getDatabaseAdminClient() { return env.getTestHelper().getClient().getDatabaseAdminClient(); } + private DatabaseClient getCreatedDatabaseClient() { if (dialect.dialect == Dialect.POSTGRESQL) { - return env.getTestHelper().getDatabaseClient(this.POST_GRE_SQL_DATABASE); + return env.getTestHelper().getDatabaseClient(this.POSTGRESQL_DATABASE); } return env.getTestHelper().getDatabaseClient(this.GOOGLE_STANDARD_SQL_DATABASE); } diff --git a/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java index 522600cf350..1caf26fb289 100644 --- a/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,19 +17,10 @@ package com.example.spanner; // [START spanner_alter_table_with_foreign_key_delete_cascade] -import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.spanner.DatabaseAdminClient; -import com.google.cloud.spanner.Instance; -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.InstanceConfigId; -import com.google.cloud.spanner.InstanceId; -import com.google.cloud.spanner.InstanceInfo; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; import com.google.common.collect.ImmutableList; -import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; -import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; -import java.util.concurrent.ExecutionException; class AlterTableWithForeignKeyDeleteCascadeSample { @@ -49,18 +40,20 @@ static void alterForeignKeyDeleteCascadeConstraint() { static void alterForeignKeyDeleteCascadeConstraint( DatabaseAdminClient adminClient, String instanceId, String databaseId) { adminClient.updateDatabaseDdl( - instanceId, - databaseId, - ImmutableList.of("ALTER TABLE ShoppingCarts\n" + instanceId, + databaseId, + ImmutableList.of( + "ALTER TABLE ShoppingCarts\n" + " ADD CONSTRAINT FKShoppingCartsCustomerName\n" + " FOREIGN KEY (CustomerName)\n" + " REFERENCES Customers(CustomerName)\n" + " ON DELETE CASCADE\n"), - null); - - System.out.printf(String.format("Altered ShoppingCarts table with FKShoppingCartsCustomerName\n" - + "foreign key constraint on database %s on instance %s", databaseId, instanceId)); - + null); + System.out.printf( + String.format( + "Altered ShoppingCarts table with FKShoppingCartsCustomerName\n" + + "foreign key constraint on database %s on instance %s", + databaseId, instanceId)); } } // [END spanner_alter_table_with_foreign_key_delete_cascade] diff --git a/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java index 0e21865df7c..dda09591ee4 100644 --- a/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,18 +17,10 @@ package com.example.spanner; // [START spanner_create_table_with_foreign_key_delete_cascade] -import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.spanner.DatabaseAdminClient; -import com.google.cloud.spanner.Instance; -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.InstanceConfigId; -import com.google.cloud.spanner.InstanceId; -import com.google.cloud.spanner.InstanceInfo; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; import com.google.common.collect.ImmutableList; -import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; -import java.util.concurrent.ExecutionException; class CreateTableWithForeignKeyDeleteCascadeSample { @@ -50,10 +42,11 @@ static void createForeignKeyDeleteCascadeConstraint( adminClient.updateDatabaseDdl( instanceId, databaseId, - ImmutableList.of("CREATE TABLE Customers (\n" - + " CustomerId INT64 NOT NULL,\n" - + " CustomerName STRING(62) NOT NULL,\n" - + " ) PRIMARY KEY (CustomerId)", + ImmutableList.of( + "CREATE TABLE Customers (\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " ) PRIMARY KEY (CustomerId)", "CREATE TABLE ShoppingCarts (\n" + " CartId INT64 NOT NULL,\n" + " CustomerId INT64 NOT NULL,\n" @@ -63,8 +56,11 @@ static void createForeignKeyDeleteCascadeConstraint( + " ) PRIMARY KEY (CartId)\n"), null); - System.out.printf(String.format("Created Customers and ShoppingCarts table with FKShoppingCartsCustomerId\n" - + "foreign key constraint on database %s on instance %s\n", databaseId, instanceId)); + System.out.printf( + String.format( + "Created Customers and ShoppingCarts table with FKShoppingCartsCustomerId\n" + + "foreign key constraint on database %s on instance %s\n", + databaseId, instanceId)); } } // [END spanner_create_table_with_foreign_key_delete_cascade] diff --git a/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java b/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java index aff0b624e1d..13f39d129f7 100644 --- a/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java +++ b/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,18 +17,10 @@ package com.example.spanner; // [START spanner_drop_foreign_key_constraint_delete_cascade] -import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.spanner.DatabaseAdminClient; -import com.google.cloud.spanner.Instance; -import com.google.cloud.spanner.InstanceAdminClient; -import com.google.cloud.spanner.InstanceConfigId; -import com.google.cloud.spanner.InstanceId; -import com.google.cloud.spanner.InstanceInfo; import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; import com.google.common.collect.ImmutableList; -import com.google.spanner.admin.instance.v1.CreateInstanceMetadata; -import java.util.concurrent.ExecutionException; class DropForeignKeyConstraintDeleteCascadeSample { @@ -50,13 +42,16 @@ static void deleteForeignKeyDeleteCascadeConstraint( adminClient.updateDatabaseDdl( instanceId, databaseId, - ImmutableList.of("ALTER TABLE ShoppingCarts\n" - + " DROP CONSTRAINT FKShoppingCartsCustomerName\n"), + ImmutableList.of( + "ALTER TABLE ShoppingCarts\n" + + " DROP CONSTRAINT FKShoppingCartsCustomerName\n"), null); - System.out.printf(String.format("Altered ShoppingCarts table to drop FKShoppingCartsCustomerName\n" - + "foreign key constraint on database %s on instance %s\n", databaseId, instanceId)); - + System.out.printf( + String.format( + "Altered ShoppingCarts table to drop FKShoppingCartsCustomerName\n" + + "foreign key constraint on database %s on instance %s\n", + databaseId, instanceId)); } } -// [END spanner_drop_foreign_key_constraint_delete_cascade] \ No newline at end of file +// [END spanner_drop_foreign_key_constraint_delete_cascade] diff --git a/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java index 4f32b6dff9f..9022a409e9f 100644 --- a/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java @@ -1,3 +1,19 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.example.spanner; import static org.junit.Assert.assertTrue; @@ -13,29 +29,36 @@ public void testAlterTableWithForeignKeyDeleteCascade() throws Exception { // Creates database final String databaseId = idGenerator.generateDatabaseId(); - databaseAdminClient.createDatabase(instanceId, databaseId, Arrays.asList("CREATE TABLE Customers (\n" - + " CustomerId INT64 NOT NULL,\n" - + " CustomerName STRING(62) NOT NULL,\n" - + " ) PRIMARY KEY (CustomerId)", - "CREATE TABLE ShoppingCarts (\n" - + " CartId INT64 NOT NULL,\n" - + " CustomerId INT64 NOT NULL,\n" - + " CustomerName STRING(62) NOT NULL,\n" - + " CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId)\n" - + " REFERENCES Customers (CustomerId) ON DELETE CASCADE\n" - + " ) PRIMARY KEY (CartId)\n") - ).get(5, TimeUnit.MINUTES); + databaseAdminClient + .createDatabase( + instanceId, + databaseId, + Arrays.asList( + "CREATE TABLE Customers (\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " ) PRIMARY KEY (CustomerId)", + "CREATE TABLE ShoppingCarts (\n" + + " CartId INT64 NOT NULL,\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId)\n" + + " REFERENCES Customers (CustomerId) ON DELETE CASCADE\n" + + " ) PRIMARY KEY (CartId)\n")) + .get(5, TimeUnit.MINUTES); // Runs sample - final String out = SampleRunner.runSample(() -> AlterTableWithForeignKeyDeleteCascadeSample - .alterForeignKeyDeleteCascadeConstraint(databaseAdminClient, instanceId, databaseId) - ); + final String out = + SampleRunner.runSample( + () -> + AlterTableWithForeignKeyDeleteCascadeSample.alterForeignKeyDeleteCascadeConstraint( + databaseAdminClient, instanceId, databaseId)); assertTrue( - "Expected to have created database " + databaseId + " with tables containing " - + "foreign key constraints.", out.contains("Altered ShoppingCarts table " - + "with FKShoppingCartsCustomerName" - ) - ); + "Expected to have created database " + + databaseId + + " with tables containing " + + "foreign key constraints.", + out.contains("Altered ShoppingCarts table " + "with FKShoppingCartsCustomerName")); } -} \ No newline at end of file +} diff --git a/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java index 8e02d74c761..0cfc2d7e10b 100644 --- a/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java @@ -1,3 +1,19 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.example.spanner; import static org.junit.Assert.assertTrue; @@ -13,28 +29,34 @@ public void testDropForeignKeyConstraintDeleteCascade() throws Exception { // Creates database final String databaseId = idGenerator.generateDatabaseId(); - databaseAdminClient.createDatabase(instanceId, databaseId, Arrays.asList("CREATE TABLE Customers (\n" - + " CustomerId INT64 NOT NULL,\n" - + " CustomerName STRING(62) NOT NULL,\n" - + " ) PRIMARY KEY (CustomerId)", - "CREATE TABLE ShoppingCarts (\n" - + " CartId INT64 NOT NULL,\n" - + " CustomerId INT64 NOT NULL,\n" - + " CustomerName STRING(62) NOT NULL,\n" - + " CONSTRAINT FKShoppingCartsCustomerName FOREIGN KEY (CustomerName)\n" - + " REFERENCES Customers (CustomerName) ON DELETE CASCADE\n" - + " ) PRIMARY KEY (CartId)\n") - ).get(5, TimeUnit.MINUTES); + databaseAdminClient + .createDatabase( + instanceId, + databaseId, + Arrays.asList( + "CREATE TABLE Customers (\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " ) PRIMARY KEY (CustomerId)", + "CREATE TABLE ShoppingCarts (\n" + + " CartId INT64 NOT NULL,\n" + + " CustomerId INT64 NOT NULL,\n" + + " CustomerName STRING(62) NOT NULL,\n" + + " CONSTRAINT FKShoppingCartsCustomerName FOREIGN KEY (CustomerName)\n" + + " REFERENCES Customers (CustomerName) ON DELETE CASCADE\n" + + " ) PRIMARY KEY (CartId)\n")) + .get(5, TimeUnit.MINUTES); // Runs sample - final String out = SampleRunner.runSample(() -> DropForeignKeyConstraintDeleteCascadeSample - .deleteForeignKeyDeleteCascadeConstraint(databaseAdminClient, instanceId, databaseId) - ); + final String out = + SampleRunner.runSample( + () -> + DropForeignKeyConstraintDeleteCascadeSample.deleteForeignKeyDeleteCascadeConstraint( + databaseAdminClient, instanceId, databaseId)); assertTrue( "Expected to have dropped foreign-key constraints from tables in created database " - + databaseId , out.contains("Altered ShoppingCarts table to drop FKShoppingCartsCustomerName" - ) - ); + + databaseId, + out.contains("Altered ShoppingCarts table to drop FKShoppingCartsCustomerName")); } -} \ No newline at end of file +} From 7bc9494790b415c6e5a7b51190df299b3a6deccc Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Mon, 27 Mar 2023 10:48:35 +0000 Subject: [PATCH 06/11] =?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 | 9 +- .../it/ITForeignKeyDeleteCascadeTest.java | 283 +++++++++++------- 2 files changed, 174 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index 83b6054f5d1..ee8c9338c82 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:26.10.0') +implementation platform('com.google.cloud:libraries-bom:26.11.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.37.0' +implementation 'com.google.cloud:google-cloud-spanner:6.38.0' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.37.0" +libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.38.0" ``` ## Authentication @@ -247,6 +247,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | Add Json Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AddJsonColumnSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/AddJsonColumnSample.java) | | Add Jsonb Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AddJsonbColumnSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/AddJsonbColumnSample.java) | | Add Numeric Column Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AddNumericColumnSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/AddNumericColumnSample.java) | +| Alter Table With Foreign Key Delete Cascade Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSample.java) | | Async Dml Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AsyncDmlExample.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/AsyncDmlExample.java) | | Async Query Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AsyncQueryExample.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/AsyncQueryExample.java) | | Async Query To List Async Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/AsyncQueryToListAsyncExample.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/AsyncQueryToListAsyncExample.java) | @@ -265,9 +266,11 @@ Samples are in the [`samples/`](https://github.com/googleapis/java-spanner/tree/ | Create Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/CreateInstanceConfigSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/CreateInstanceConfigSample.java) | | Create Instance Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/CreateInstanceExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/CreateInstanceExample.java) | | Create Instance With Processing Units Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithProcessingUnitsExample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/CreateInstanceWithProcessingUnitsExample.java) | +| Create Table With Foreign Key Delete Cascade Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSample.java) | | Custom Timeout And Retry Settings Example | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/CustomTimeoutAndRetrySettingsExample.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/CustomTimeoutAndRetrySettingsExample.java) | | Delete Instance Config Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/DeleteInstanceConfigSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/DeleteInstanceConfigSample.java) | | Delete Using Dml Returning Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/DeleteUsingDmlReturningSample.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/DeleteUsingDmlReturningSample.java) | +| Drop Foreign Key Constraint Delete Cascade Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSample.java) | | Enable Fine Grained Access | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/EnableFineGrainedAccess.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/EnableFineGrainedAccess.java) | | Get Commit Stats Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/GetCommitStatsSample.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/GetCommitStatsSample.java) | | Get Database Ddl Sample | [source code](https://github.com/googleapis/java-spanner/blob/main/samples/snippets/src/main/java/com/example/spanner/GetDatabaseDdlSample.java) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/java-spanner&page=editor&open_in_editor=samples/snippets/src/main/java/com/example/spanner/GetDatabaseDdlSample.java) | diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java index 886eaa389e5..a3ea6f50a85 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java @@ -48,8 +48,7 @@ @RunWith(Parameterized.class) public class ITForeignKeyDeleteCascadeTest { - @ClassRule - public static IntegrationTestEnv env = new IntegrationTestEnv(); + @ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv(); @Parameterized.Parameters(name = "Dialect = {0}") public static List data() { @@ -64,21 +63,24 @@ public static List data() { private static final String DELETE_RULE_CASCADE = "CASCADE"; private static final String DELETE_RULE_DEFAULT = "NO ACTION"; private static final String DELETE_RULE_COLUMN_NAME = "DELETE_RULE"; - private static final String CREATE_TABLE_SINGER = "CREATE TABLE Singer (\n" - + " SingerId INT64 NOT NULL,\n" - + " FirstName STRING(1024),\n" - + ") PRIMARY KEY(SingerId)\n"; - - private static final String POSTGRES_CREATE_TABLE_SINGER = "CREATE TABLE Singer (\n" - + " singer_id BIGINT PRIMARY KEY,\n" - + " first_name VARCHAR\n" - + ")"; - - private static final String CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY = "CREATE TABLE Concerts (\n" - + " VenueId INT64 NOT NULL,\n" - + " SingerId INT64 NOT NULL,\n" - + " CONSTRAINT Fk_Concerts_Singer FOREIGN KEY (SingerId) REFERENCES Singer (SingerId) ON DELETE CASCADE" - + ") PRIMARY KEY(VenueId, SingerId)"; + private static final String CREATE_TABLE_SINGER = + "CREATE TABLE Singer (\n" + + " SingerId INT64 NOT NULL,\n" + + " FirstName STRING(1024),\n" + + ") PRIMARY KEY(SingerId)\n"; + + private static final String POSTGRES_CREATE_TABLE_SINGER = + "CREATE TABLE Singer (\n" + + " singer_id BIGINT PRIMARY KEY,\n" + + " first_name VARCHAR\n" + + ")"; + + private static final String CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY = + "CREATE TABLE Concerts (\n" + + " VenueId INT64 NOT NULL,\n" + + " SingerId INT64 NOT NULL,\n" + + " CONSTRAINT Fk_Concerts_Singer FOREIGN KEY (SingerId) REFERENCES Singer (SingerId) ON DELETE CASCADE" + + ") PRIMARY KEY(VenueId, SingerId)"; private static final String POSTGRES_CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY = "CREATE TABLE Concerts (\n" @@ -101,9 +103,10 @@ public static List data() { + " PRIMARY KEY (venue_id, singer_id)\n" + " )"; - private static final String ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY = "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) " - + "ON DELETE CASCADE"; + private static final String ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY = + "ALTER TABLE ConcertsV2 " + + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) " + + "ON DELETE CASCADE"; private static final String POSTGRES_ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY = "ALTER TABLE ConcertsV2 " @@ -114,19 +117,18 @@ public static List data() { "ALTER TABLE ConcertsV2 " + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) "; - private static final String POSTGRES_ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE = - "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) "; + private static final String + POSTGRES_ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE = + "ALTER TABLE ConcertsV2 " + + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) "; private static final String ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT = - "ALTER TABLE ConcertsV2\n" - + "DROP CONSTRAINT Fk_Concerts_Singer_V2"; + "ALTER TABLE ConcertsV2\n" + "DROP CONSTRAINT Fk_Concerts_Singer_V2"; private static Database GOOGLE_STANDARD_SQL_DATABASE; private static Database POSTGRESQL_DATABASE; private static List dbs = new ArrayList<>(); - @Parameterized.Parameter(0) public DialectTestParameter dialect; @@ -134,14 +136,15 @@ public static List data() { public static void setUpDatabase() { GOOGLE_STANDARD_SQL_DATABASE = env.getTestHelper() - .createTestDatabase(ImmutableList.of( - CREATE_TABLE_SINGER, CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); + .createTestDatabase( + ImmutableList.of(CREATE_TABLE_SINGER, CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); if (!isUsingEmulator()) { POSTGRESQL_DATABASE = env.getTestHelper() .createTestDatabase( Dialect.POSTGRESQL, - ImmutableList.of(POSTGRES_CREATE_TABLE_SINGER, + ImmutableList.of( + POSTGRES_CREATE_TABLE_SINGER, POSTGRES_CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); } dbs.add(GOOGLE_STANDARD_SQL_DATABASE); @@ -161,9 +164,7 @@ public void testForeignKeyDeleteCascadeConstraints_withCreateDDLStatements() { final DatabaseClient databaseClient = getCreatedDatabaseClient(); final String referentialConstraintQuery = getReferentialConstraintsQueryStatement(); try (final ResultSet rs = - databaseClient - .singleUse() - .executeQuery(Statement.of(referentialConstraintQuery))) { + databaseClient.singleUse().executeQuery(Statement.of(referentialConstraintQuery))) { while (rs.next()) { assertThat(rs.getString(DELETE_RULE_COLUMN_NAME)).isEqualTo(DELETE_RULE_CASCADE); } @@ -172,7 +173,8 @@ public void testForeignKeyDeleteCascadeConstraints_withCreateDDLStatements() { @Test public void testForeignKeyDeleteCascadeConstraints_withAlterDDLStatements() throws Exception { - // Creating new tables within this test to ensure we don't pollute tables used by other tests in this class. + // Creating new tables within this test to ensure we don't pollute tables used by other tests in + // this class. final List createStatements = getCreateAndAlterTableStatementsWithForeignKey(); final Database createdDatabase = env.getTestHelper().createTestDatabase(dialect.dialect, createStatements); @@ -182,10 +184,7 @@ public void testForeignKeyDeleteCascadeConstraints_withAlterDDLStatements() thro final String referentialConstraintQuery = getReferentialConstraintsQueryStatement(); try (final ResultSet rs = - databaseClient - .singleUse() - .executeQuery( - Statement.of(referentialConstraintQuery))) { + databaseClient.singleUse().executeQuery(Statement.of(referentialConstraintQuery))) { while (rs.next()) { assertThat(rs.getString(DELETE_RULE_COLUMN_NAME)).isEqualTo(DELETE_RULE_CASCADE); } @@ -194,8 +193,11 @@ public void testForeignKeyDeleteCascadeConstraints_withAlterDDLStatements() thro // remove the foreign key delete cascade constraint final List alterDropStatements = getAlterDropForeignKeyDeleteCascadeStatements(); getDatabaseAdminClient() - .updateDatabaseDdl(env.getTestHelper().getInstanceId().getInstance(), - createdDatabase.getId().getDatabase(), alterDropStatements, null) + .updateDatabaseDdl( + env.getTestHelper().getInstanceId().getInstance(), + createdDatabase.getId().getDatabase(), + alterDropStatements, + null) .get(); try (final ResultSet rs = @@ -213,16 +215,24 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { final DatabaseClient databaseClient = getCreatedDatabaseClient(); final String singerInsertStatement = getInsertStatementForSingerTable(); - final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) - // Use 'p1' to bind to the parameter with index 1 etc. - .bind("p1").to(1L) - .bind("p2").to("singerName").build(); + final Statement singerInsertStatementWithValues = + Statement.newBuilder(singerInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1") + .to(1L) + .bind("p2") + .to("singerName") + .build(); final String concertInsertStatement = getInsertStatementForConcertsTable(); - final Statement concertInsertStatementWithValues = Statement.newBuilder(concertInsertStatement) - // Use 'p1' to bind to the parameter with index 1 etc. - .bind("p1").to(1L) - .bind("p2").to(1L).build(); + final Statement concertInsertStatementWithValues = + Statement.newBuilder(concertInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1") + .to(1L) + .bind("p2") + .to(1L) + .build(); // successful inserts into referenced and referencing tables databaseClient @@ -230,8 +240,8 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { .run( transaction -> { transaction.batchUpdate( - ImmutableList.of(singerInsertStatementWithValues, - concertInsertStatementWithValues)); + ImmutableList.of( + singerInsertStatementWithValues, concertInsertStatementWithValues)); return null; }); @@ -240,7 +250,8 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { final String concertVenueIdColumnName = getConcertVenueIdColumnName(); try (ResultSet resultSet = - databaseClient.singleUse() + databaseClient + .singleUse() .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_SINGER))) { resultSet.next(); @@ -251,7 +262,8 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { } try (ResultSet resultSet = - databaseClient.singleUse() + databaseClient + .singleUse() .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_CONCERTS))) { resultSet.next(); @@ -266,22 +278,29 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { public void testForeignKeyDeleteCascadeConstraints_verifyInvalidInsertions() { final DatabaseClient databaseClient = getCreatedDatabaseClient(); - // unsuccessful inserts into referencing tables when foreign key is not inserted into referenced table + // unsuccessful inserts into referencing tables when foreign key is not inserted into referenced + // table final String concertInsertStatement = getInsertStatementForConcertsTable(); - final Statement concertInsertStatementWithInvalidValues = Statement.newBuilder( - concertInsertStatement) - // Use 'p1' to bind to the parameter with index 1 etc. - .bind("p1").to(2L) - .bind("p2").to(2L).build(); - - SpannerException ex = assertThrows(SpannerException.class, () -> - databaseClient - .readWriteTransaction() - .run( - transaction -> { - transaction.executeUpdate(concertInsertStatementWithInvalidValues); - return null; - })); + final Statement concertInsertStatementWithInvalidValues = + Statement.newBuilder(concertInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1") + .to(2L) + .bind("p2") + .to(2L) + .build(); + + SpannerException ex = + assertThrows( + SpannerException.class, + () -> + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.executeUpdate(concertInsertStatementWithInvalidValues); + return null; + })); assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); assertThat(ex.getMessage()).contains("Cannot find referenced values"); } @@ -292,16 +311,24 @@ public void testForeignKeyDeleteCascadeConstraints_forDeletions() { final DatabaseClient databaseClient = getCreatedDatabaseClient(); final String singerInsertStatement = getInsertStatementForSingerTable(); - final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) - // Use 'p1' to bind to the parameter with index 1 etc. - .bind("p1").to(3L) - .bind("p2").to("singerName").build(); + final Statement singerInsertStatementWithValues = + Statement.newBuilder(singerInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1") + .to(3L) + .bind("p2") + .to("singerName") + .build(); final String concertInsertStatement = getInsertStatementForConcertsTable(); - final Statement concertInsertStatementWithValues = Statement.newBuilder(concertInsertStatement) - // Use 'p1' to bind to the parameter with index 1 etc. - .bind("p1").to(3L) - .bind("p2").to(3L).build(); + final Statement concertInsertStatementWithValues = + Statement.newBuilder(concertInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1") + .to(3L) + .bind("p2") + .to(3L) + .build(); // successful inserts into referenced and referencing tables databaseClient @@ -309,16 +336,19 @@ public void testForeignKeyDeleteCascadeConstraints_forDeletions() { .run( transaction -> { transaction.batchUpdate( - ImmutableList.of(singerInsertStatementWithValues, - concertInsertStatementWithValues)); + ImmutableList.of( + singerInsertStatementWithValues, concertInsertStatementWithValues)); return null; }); // execute delete final String singerDeleteStatement = getDeleteStatementForSingerTable(); - final Statement singerDeleteStatementWithValues = Statement.newBuilder(singerDeleteStatement) - // Use 'p1' to bind to the parameter with index 1 etc. - .bind("p1").to(3L).build(); + final Statement singerDeleteStatementWithValues = + Statement.newBuilder(singerDeleteStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1") + .to(3L) + .build(); databaseClient .readWriteTransaction() .run( @@ -328,13 +358,15 @@ public void testForeignKeyDeleteCascadeConstraints_forDeletions() { }); try (ResultSet resultSet = - databaseClient.singleUse() + databaseClient + .singleUse() .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_SINGER))) { assertThat(resultSet.next()).isFalse(); } try (ResultSet resultSet = - databaseClient.singleUse() + databaseClient + .singleUse() .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_CONCERTS))) { assertThat(resultSet.next()).isFalse(); } @@ -346,19 +378,25 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictDueToP // inserting and deleting the referenced key within the same mutation are considered // conflicting operations, thus this results in an exception. - SpannerException ex = assertThrows(SpannerException.class, () -> - databaseClient - .readWriteTransaction() - .run( - transaction -> { - transaction.buffer( - Arrays.asList( - Mutation.newInsertBuilder("Singer") - .set(getSingerIdColumnName()).to(4L) - .set(getSingerFirstNameColumnName()).to("singerName").build(), - Mutation.delete("Singer", Key.of(4L)))); - return null; - })); + SpannerException ex = + assertThrows( + SpannerException.class, + () -> + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.buffer( + Arrays.asList( + Mutation.newInsertBuilder("Singer") + .set(getSingerIdColumnName()) + .to(4L) + .set(getSingerFirstNameColumnName()) + .to("singerName") + .build(), + Mutation.delete("Singer", Key.of(4L)))); + return null; + })); assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); } @@ -369,10 +407,14 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictsDueTo // referencing a foreign key in child table and deleting the referenced key in parent table // within the same mutations are considered conflicting operations. final String singerInsertStatement = getInsertStatementForSingerTable(); - final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) - // Use 'p1' to bind to the parameter with index 1 etc. - .bind("p1").to(5L) - .bind("p2").to("singerName").build(); + final Statement singerInsertStatementWithValues = + Statement.newBuilder(singerInsertStatement) + // Use 'p1' to bind to the parameter with index 1 etc. + .bind("p1") + .to(5L) + .bind("p2") + .to("singerName") + .build(); databaseClient .readWriteTransaction() @@ -381,19 +423,25 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictsDueTo transaction.executeUpdate(singerInsertStatementWithValues); return null; }); - SpannerException ex = assertThrows(SpannerException.class, () -> - databaseClient - .readWriteTransaction() - .run( - transaction -> { - transaction.buffer( - Arrays.asList( - Mutation.newInsertBuilder("Concerts") - .set(getConcertVenueIdColumnName()).to(5L) - .set(getSingerIdColumnName()).to(5L).build(), - Mutation.delete("Singer", Key.of(5L)))); - return null; - })); + SpannerException ex = + assertThrows( + SpannerException.class, + () -> + databaseClient + .readWriteTransaction() + .run( + transaction -> { + transaction.buffer( + Arrays.asList( + Mutation.newInsertBuilder("Concerts") + .set(getConcertVenueIdColumnName()) + .to(5L) + .set(getSingerIdColumnName()) + .to(5L) + .build(), + Mutation.delete("Singer", Key.of(5L)))); + return null; + })); } private String getReferentialConstraintsQueryStatement() { @@ -422,21 +470,26 @@ private String getReferentialConstraintsQueryStatementV2() { private List getCreateAndAlterTableStatementsWithForeignKey() { if (dialect.dialect == Dialect.POSTGRESQL) { - return ImmutableList.of(POSTGRES_CREATE_TABLE_SINGER, + return ImmutableList.of( + POSTGRES_CREATE_TABLE_SINGER, POSTGRES_CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY, POSTGRES_ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY); } else { - return ImmutableList.of(CREATE_TABLE_SINGER, CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY, + return ImmutableList.of( + CREATE_TABLE_SINGER, + CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY, ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY); } } private List getAlterDropForeignKeyDeleteCascadeStatements() { if (dialect.dialect == Dialect.POSTGRESQL) { - return ImmutableList.of(ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT, + return ImmutableList.of( + ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT, POSTGRES_ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE); } else { - return ImmutableList.of(ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT, + return ImmutableList.of( + ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT, ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE); } } From 456ed31d9683396bff683fd27388524402194cfa Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 27 Mar 2023 16:29:41 +0530 Subject: [PATCH 07/11] fix: lint issues and exlude tests from emulator. --- .../it/ITForeignKeyDeleteCascadeTest.java | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java index a3ea6f50a85..21c29f76352 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java @@ -15,10 +15,10 @@ */ package com.google.cloud.spanner.it; -import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeFalse; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseAdminClient; @@ -32,6 +32,7 @@ import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.Statement; +import com.google.cloud.spanner.testing.EmulatorSpannerHelper; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Arrays; @@ -134,11 +135,11 @@ public static List data() { @BeforeClass public static void setUpDatabase() { - GOOGLE_STANDARD_SQL_DATABASE = - env.getTestHelper() - .createTestDatabase( - ImmutableList.of(CREATE_TABLE_SINGER, CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); - if (!isUsingEmulator()) { + if (!EmulatorSpannerHelper.isUsingEmulator()) { + GOOGLE_STANDARD_SQL_DATABASE = + env.getTestHelper() + .createTestDatabase( + ImmutableList.of(CREATE_TABLE_SINGER, CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); POSTGRESQL_DATABASE = env.getTestHelper() .createTestDatabase( @@ -146,9 +147,10 @@ public static void setUpDatabase() { ImmutableList.of( POSTGRES_CREATE_TABLE_SINGER, POSTGRES_CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); + + dbs.add(GOOGLE_STANDARD_SQL_DATABASE); + dbs.add(POSTGRESQL_DATABASE); } - dbs.add(GOOGLE_STANDARD_SQL_DATABASE); - dbs.add(POSTGRESQL_DATABASE); } @AfterClass @@ -161,6 +163,10 @@ public static void tearDown() { @Test public void testForeignKeyDeleteCascadeConstraints_withCreateDDLStatements() { + assumeFalse( + "Emulator does not yet support foreign key delete cascade", + EmulatorSpannerHelper.isUsingEmulator()); + final DatabaseClient databaseClient = getCreatedDatabaseClient(); final String referentialConstraintQuery = getReferentialConstraintsQueryStatement(); try (final ResultSet rs = @@ -173,6 +179,9 @@ public void testForeignKeyDeleteCascadeConstraints_withCreateDDLStatements() { @Test public void testForeignKeyDeleteCascadeConstraints_withAlterDDLStatements() throws Exception { + assumeFalse( + "Emulator does not yet support foreign key delete cascade", + EmulatorSpannerHelper.isUsingEmulator()); // Creating new tables within this test to ensure we don't pollute tables used by other tests in // this class. final List createStatements = getCreateAndAlterTableStatementsWithForeignKey(); @@ -212,6 +221,9 @@ public void testForeignKeyDeleteCascadeConstraints_withAlterDDLStatements() thro @Test public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { + assumeFalse( + "Emulator does not yet support foreign key delete cascade", + EmulatorSpannerHelper.isUsingEmulator()); final DatabaseClient databaseClient = getCreatedDatabaseClient(); final String singerInsertStatement = getInsertStatementForSingerTable(); @@ -276,6 +288,10 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { @Test public void testForeignKeyDeleteCascadeConstraints_verifyInvalidInsertions() { + assumeFalse( + "Emulator does not yet support foreign key delete cascade", + EmulatorSpannerHelper.isUsingEmulator()); + final DatabaseClient databaseClient = getCreatedDatabaseClient(); // unsuccessful inserts into referencing tables when foreign key is not inserted into referenced @@ -307,6 +323,9 @@ public void testForeignKeyDeleteCascadeConstraints_verifyInvalidInsertions() { @Test public void testForeignKeyDeleteCascadeConstraints_forDeletions() { + assumeFalse( + "Emulator does not yet support foreign key delete cascade", + EmulatorSpannerHelper.isUsingEmulator()); final DatabaseClient databaseClient = getCreatedDatabaseClient(); @@ -374,6 +393,10 @@ public void testForeignKeyDeleteCascadeConstraints_forDeletions() { @Test public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictDueToParentTable() { + assumeFalse( + "Emulator does not yet support foreign key delete cascade", + EmulatorSpannerHelper.isUsingEmulator()); + final DatabaseClient databaseClient = getCreatedDatabaseClient(); // inserting and deleting the referenced key within the same mutation are considered @@ -402,6 +425,10 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictDueToP @Test public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictsDueToChildTable() { + assumeFalse( + "Emulator does not yet support foreign key delete cascade", + EmulatorSpannerHelper.isUsingEmulator()); + final DatabaseClient databaseClient = getCreatedDatabaseClient(); // referencing a foreign key in child table and deleting the referenced key in parent table From b0db5047b0de7c8684f8079cf96507464da1e7cc Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 27 Mar 2023 16:51:13 +0530 Subject: [PATCH 08/11] fix : checkstyle issues in samples. --- ...leWithForeignKeyDeleteCascadeSampleIT.java | 3 ++- ...leWithForeignKeyDeleteCascadeSampleIT.java | 27 +++++++++++-------- ...ignKeyConstraintDeleteCascadeSampleIT.java | 3 ++- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java index 9022a409e9f..53aa5212fa1 100644 --- a/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java @@ -42,7 +42,8 @@ public void testAlterTableWithForeignKeyDeleteCascade() throws Exception { + " CartId INT64 NOT NULL,\n" + " CustomerId INT64 NOT NULL,\n" + " CustomerName STRING(62) NOT NULL,\n" - + " CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId)\n" + + " CONSTRAINT FKShoppingCartsCustomerId" + + " FOREIGN KEY (CustomerId)\n" + " REFERENCES Customers (CustomerId) ON DELETE CASCADE\n" + " ) PRIMARY KEY (CartId)\n")) .get(5, TimeUnit.MINUTES); diff --git a/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java index dbe768cf81b..730511afe58 100644 --- a/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java @@ -13,19 +13,24 @@ public void testCreateTableWithForeignKeyDeleteCascade() throws Exception { // Creates database final String databaseId = idGenerator.generateDatabaseId(); - databaseAdminClient.createDatabase(instanceId, databaseId, Arrays.asList() - ).get(5, TimeUnit.MINUTES); + databaseAdminClient + .createDatabase(instanceId, databaseId, Arrays.asList()) + .get(5, TimeUnit.MINUTES); // Runs sample - final String out = SampleRunner.runSample(() -> CreateTableWithForeignKeyDeleteCascadeSample - .createForeignKeyDeleteCascadeConstraint(databaseAdminClient, instanceId, databaseId) - ); + final String out = + SampleRunner.runSample( + () -> + CreateTableWithForeignKeyDeleteCascadeSample + .createForeignKeyDeleteCascadeConstraint( + databaseAdminClient, instanceId, databaseId)); assertTrue( - "Expected to have created database " + databaseId + " with tables containing " - + "foreign key constraints.", out.contains("Created Customers and ShoppingCarts table " - + "with FKShoppingCartsCustomerId" - ) - ); + "Expected to have created database " + + databaseId + + " with tables containing " + + "foreign key constraints.", + out.contains( + "Created Customers and ShoppingCarts table " + "with FKShoppingCartsCustomerId")); } -} \ No newline at end of file +} diff --git a/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java index 0cfc2d7e10b..1c58daded18 100644 --- a/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/DropForeignKeyConstraintDeleteCascadeSampleIT.java @@ -42,7 +42,8 @@ public void testDropForeignKeyConstraintDeleteCascade() throws Exception { + " CartId INT64 NOT NULL,\n" + " CustomerId INT64 NOT NULL,\n" + " CustomerName STRING(62) NOT NULL,\n" - + " CONSTRAINT FKShoppingCartsCustomerName FOREIGN KEY (CustomerName)\n" + + " CONSTRAINT FKShoppingCartsCustomerName" + + " FOREIGN KEY (CustomerName)\n" + " REFERENCES Customers (CustomerName) ON DELETE CASCADE\n" + " ) PRIMARY KEY (CartId)\n")) .get(5, TimeUnit.MINUTES); From 45a369d60a5f5cd960477d535abd607ec67e3e9a Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 27 Mar 2023 18:02:28 +0530 Subject: [PATCH 09/11] fix: add copyright for test. --- ...TableWithForeignKeyDeleteCascadeSampleIT.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java index 730511afe58..b2dd6638cea 100644 --- a/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/CreateTableWithForeignKeyDeleteCascadeSampleIT.java @@ -1,3 +1,19 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.example.spanner; import static org.junit.Assert.assertTrue; From 6cc39caf786ef630a57743c5996f4823c5ae99b0 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Wed, 29 Mar 2023 16:39:34 +0530 Subject: [PATCH 10/11] fix:review comments. --- .../it/ITForeignKeyDeleteCascadeTest.java | 332 +++++++----------- 1 file changed, 132 insertions(+), 200 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java index 21c29f76352..fc7c860267a 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITForeignKeyDeleteCascadeTest.java @@ -15,9 +15,10 @@ */ package com.google.cloud.spanner.it; -import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import com.google.cloud.spanner.Database; @@ -37,6 +38,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -60,71 +63,8 @@ public static List data() { } private static final String TABLE_NAME_SINGER = "Singer"; - private static final String TABLE_NAME_CONCERTS = "Concerts"; - private static final String DELETE_RULE_CASCADE = "CASCADE"; - private static final String DELETE_RULE_DEFAULT = "NO ACTION"; + private static final String TABLE_NAME_CONCERT = "Concert"; private static final String DELETE_RULE_COLUMN_NAME = "DELETE_RULE"; - private static final String CREATE_TABLE_SINGER = - "CREATE TABLE Singer (\n" - + " SingerId INT64 NOT NULL,\n" - + " FirstName STRING(1024),\n" - + ") PRIMARY KEY(SingerId)\n"; - - private static final String POSTGRES_CREATE_TABLE_SINGER = - "CREATE TABLE Singer (\n" - + " singer_id BIGINT PRIMARY KEY,\n" - + " first_name VARCHAR\n" - + ")"; - - private static final String CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY = - "CREATE TABLE Concerts (\n" - + " VenueId INT64 NOT NULL,\n" - + " SingerId INT64 NOT NULL,\n" - + " CONSTRAINT Fk_Concerts_Singer FOREIGN KEY (SingerId) REFERENCES Singer (SingerId) ON DELETE CASCADE" - + ") PRIMARY KEY(VenueId, SingerId)"; - - private static final String POSTGRES_CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY = - "CREATE TABLE Concerts (\n" - + " venue_id BIGINT NOT NULL,\n" - + " singer_id BIGINT NOT NULL,\n" - + " PRIMARY KEY (venue_id, singer_id),\n" - + " CONSTRAINT Fk_Concerts_Singer FOREIGN KEY (singer_id) REFERENCES Singer (singer_id) ON DELETE CASCADE\n" - + " )"; - - private static final String CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY = - "CREATE TABLE ConcertsV2 (\n" - + " VenueId INT64 NOT NULL,\n" - + " SingerId INT64 NOT NULL,\n" - + ") PRIMARY KEY(VenueId, SingerId)"; - - private static final String POSTGRES_CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY = - "CREATE TABLE ConcertsV2 (\n" - + " venue_id BIGINT NOT NULL,\n" - + " singer_id BIGINT NOT NULL,\n" - + " PRIMARY KEY (venue_id, singer_id)\n" - + " )"; - - private static final String ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY = - "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) " - + "ON DELETE CASCADE"; - - private static final String POSTGRES_ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY = - "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) " - + "ON DELETE CASCADE"; - - private static final String ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE = - "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(SingerId) REFERENCES Singer(SingerId) "; - - private static final String - POSTGRES_ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE = - "ALTER TABLE ConcertsV2 " - + "ADD CONSTRAINT Fk_Concerts_Singer_V2 FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) "; - - private static final String ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT = - "ALTER TABLE ConcertsV2\n" + "DROP CONSTRAINT Fk_Concerts_Singer_V2"; private static Database GOOGLE_STANDARD_SQL_DATABASE; private static Database POSTGRESQL_DATABASE; @@ -139,14 +79,31 @@ public static void setUpDatabase() { GOOGLE_STANDARD_SQL_DATABASE = env.getTestHelper() .createTestDatabase( - ImmutableList.of(CREATE_TABLE_SINGER, CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); + ImmutableList.of( + "CREATE TABLE Singer (\n" + + " singer_id INT64 NOT NULL,\n" + + " first_name STRING(1024),\n" + + ") PRIMARY KEY(singer_id)\n", + "CREATE TABLE Concert (\n" + + " venue_id INT64 NOT NULL,\n" + + " singer_id INT64 NOT NULL,\n" + + " CONSTRAINT Fk_Concert_Singer FOREIGN KEY (singer_id) REFERENCES Singer (singer_id) ON DELETE CASCADE" + + ") PRIMARY KEY(venue_id, singer_id)")); POSTGRESQL_DATABASE = env.getTestHelper() .createTestDatabase( Dialect.POSTGRESQL, ImmutableList.of( - POSTGRES_CREATE_TABLE_SINGER, - POSTGRES_CREATE_TABLE_CONCERT_WITH_FOREIGN_KEY)); + "CREATE TABLE Singer (\n" + + " singer_id BIGINT PRIMARY KEY,\n" + + " first_name VARCHAR\n" + + ")", + "CREATE TABLE Concert (\n" + + " venue_id BIGINT NOT NULL,\n" + + " singer_id BIGINT NOT NULL,\n" + + " PRIMARY KEY (venue_id, singer_id),\n" + + " CONSTRAINT Fk_Concert_Singer FOREIGN KEY (singer_id) REFERENCES Singer (singer_id) ON DELETE CASCADE\n" + + " )")); dbs.add(GOOGLE_STANDARD_SQL_DATABASE); dbs.add(POSTGRESQL_DATABASE); @@ -168,11 +125,16 @@ public void testForeignKeyDeleteCascadeConstraints_withCreateDDLStatements() { EmulatorSpannerHelper.isUsingEmulator()); final DatabaseClient databaseClient = getCreatedDatabaseClient(); - final String referentialConstraintQuery = getReferentialConstraintsQueryStatement(); try (final ResultSet rs = - databaseClient.singleUse().executeQuery(Statement.of(referentialConstraintQuery))) { + databaseClient + .singleUse() + .executeQuery( + Statement.of( + "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME ='Fk_Concert_Singer'"))) { while (rs.next()) { - assertThat(rs.getString(DELETE_RULE_COLUMN_NAME)).isEqualTo(DELETE_RULE_CASCADE); + assertEquals(rs.getString(DELETE_RULE_COLUMN_NAME), "CASCADE"); } } } @@ -184,37 +146,78 @@ public void testForeignKeyDeleteCascadeConstraints_withAlterDDLStatements() thro EmulatorSpannerHelper.isUsingEmulator()); // Creating new tables within this test to ensure we don't pollute tables used by other tests in // this class. - final List createStatements = getCreateAndAlterTableStatementsWithForeignKey(); + List createStatements; + if (dialect.dialect == Dialect.POSTGRESQL) { + createStatements = + ImmutableList.of( + "CREATE TABLE Singer (\n" + + " singer_id BIGINT PRIMARY KEY,\n" + + " first_name VARCHAR\n" + + ")", + "CREATE TABLE ConcertV2 (\n" + + " venue_id BIGINT NOT NULL,\n" + + " singer_id BIGINT NOT NULL,\n" + + " PRIMARY KEY (venue_id, singer_id)\n" + + " )", + "ALTER TABLE ConcertV2 " + + "ADD CONSTRAINT Fk_Concert_Singer_V2 FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) " + + "ON DELETE CASCADE"); + } else { + createStatements = + ImmutableList.of( + "CREATE TABLE Singer (\n" + + " singer_id INT64 NOT NULL,\n" + + " first_name STRING(1024),\n" + + ") PRIMARY KEY(singer_id)\n", + "CREATE TABLE ConcertV2 (\n" + + " venue_id INT64 NOT NULL,\n" + + " singer_id INT64 NOT NULL,\n" + + ") PRIMARY KEY(venue_id, singer_id)", + "ALTER TABLE ConcertV2 " + + "ADD CONSTRAINT Fk_Concert_Singer_V2 FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) " + + "ON DELETE CASCADE"); + } final Database createdDatabase = env.getTestHelper().createTestDatabase(dialect.dialect, createStatements); dbs.add(createdDatabase); final DatabaseClient databaseClient = getCreatedDatabaseClient(); - final String referentialConstraintQuery = getReferentialConstraintsQueryStatement(); try (final ResultSet rs = - databaseClient.singleUse().executeQuery(Statement.of(referentialConstraintQuery))) { + databaseClient + .singleUse() + .executeQuery( + Statement.of( + "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME ='Fk_Concert_Singer'"))) { while (rs.next()) { - assertThat(rs.getString(DELETE_RULE_COLUMN_NAME)).isEqualTo(DELETE_RULE_CASCADE); + assertEquals(rs.getString(DELETE_RULE_COLUMN_NAME), "CASCADE"); } } // remove the foreign key delete cascade constraint - final List alterDropStatements = getAlterDropForeignKeyDeleteCascadeStatements(); getDatabaseAdminClient() .updateDatabaseDdl( env.getTestHelper().getInstanceId().getInstance(), createdDatabase.getId().getDatabase(), - alterDropStatements, + ImmutableList.of( + "ALTER TABLE ConcertV2\n" + "DROP CONSTRAINT Fk_Concert_Singer_V2", + "ALTER TABLE ConcertV2 " + + "ADD CONSTRAINT Fk_Concert_Singer_V2 FOREIGN KEY(singer_id) REFERENCES Singer(singer_id) "), null) .get(); try (final ResultSet rs = databaseClient .singleUse() - .executeQuery(Statement.of(getReferentialConstraintsQueryStatementV2()))) { + .executeQuery( + Statement.of( + "SELECT DELETE_RULE\n" + + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" + + "WHERE CONSTRAINT_NAME ='Fk_Concert_Singer_V2'"))) { while (rs.next()) { - assertThat(rs.getString(DELETE_RULE_COLUMN_NAME)).isEqualTo(DELETE_RULE_DEFAULT); + assertEquals(rs.getString(DELETE_RULE_COLUMN_NAME), "NO ACTION"); } } } @@ -226,7 +229,8 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { EmulatorSpannerHelper.isUsingEmulator()); final DatabaseClient databaseClient = getCreatedDatabaseClient(); - final String singerInsertStatement = getInsertStatementForSingerTable(); + final String singerInsertStatement = + "INSERT INTO Singer (singer_id, first_name) VALUES (" + generateQueryParameters(2) + ")"; final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) // Use 'p1' to bind to the parameter with index 1 etc. @@ -236,7 +240,8 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { .to("singerName") .build(); - final String concertInsertStatement = getInsertStatementForConcertsTable(); + final String concertInsertStatement = + "INSERT INTO Concert (venue_id, singer_id) VALUES (" + generateQueryParameters(2) + ")"; final Statement concertInsertStatementWithValues = Statement.newBuilder(concertInsertStatement) // Use 'p1' to bind to the parameter with index 1 etc. @@ -257,32 +262,28 @@ public void testForeignKeyDeleteCascadeConstraints_verifyValidInsertions() { return null; }); - final String singerIdColumnName = getSingerIdColumnName(); - final String singerFirstNameColumnName = getSingerFirstNameColumnName(); - final String concertVenueIdColumnName = getConcertVenueIdColumnName(); - try (ResultSet resultSet = databaseClient .singleUse() .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_SINGER))) { resultSet.next(); - assertEquals(1, resultSet.getLong(singerIdColumnName)); - assertEquals("singerName", resultSet.getString(singerFirstNameColumnName)); + assertEquals(1, resultSet.getLong("singer_id")); + assertEquals("singerName", resultSet.getString("first_name")); - assertThat(resultSet.next()).isFalse(); + assertFalse(resultSet.next()); } try (ResultSet resultSet = databaseClient .singleUse() - .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_CONCERTS))) { + .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_CONCERT))) { resultSet.next(); - assertEquals(1, resultSet.getLong(singerIdColumnName)); - assertEquals(1, resultSet.getLong(concertVenueIdColumnName)); + assertEquals(1, resultSet.getLong("singer_id")); + assertEquals(1, resultSet.getLong("venue_id")); - assertThat(resultSet.next()).isFalse(); + assertFalse(resultSet.next()); } } @@ -296,7 +297,8 @@ public void testForeignKeyDeleteCascadeConstraints_verifyInvalidInsertions() { // unsuccessful inserts into referencing tables when foreign key is not inserted into referenced // table - final String concertInsertStatement = getInsertStatementForConcertsTable(); + final String concertInsertStatement = + "INSERT INTO Concert (venue_id, singer_id) VALUES (" + generateQueryParameters(2) + ")"; final Statement concertInsertStatementWithInvalidValues = Statement.newBuilder(concertInsertStatement) // Use 'p1' to bind to the parameter with index 1 etc. @@ -317,8 +319,8 @@ public void testForeignKeyDeleteCascadeConstraints_verifyInvalidInsertions() { transaction.executeUpdate(concertInsertStatementWithInvalidValues); return null; })); - assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); - assertThat(ex.getMessage()).contains("Cannot find referenced values"); + assertEquals(ex.getErrorCode(), ErrorCode.FAILED_PRECONDITION); + assertTrue(ex.getMessage().contains("Cannot find referenced values")); } @Test @@ -329,7 +331,8 @@ public void testForeignKeyDeleteCascadeConstraints_forDeletions() { final DatabaseClient databaseClient = getCreatedDatabaseClient(); - final String singerInsertStatement = getInsertStatementForSingerTable(); + final String singerInsertStatement = + "INSERT INTO Singer (singer_id, first_name) VALUES (" + generateQueryParameters(2) + ")"; final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) // Use 'p1' to bind to the parameter with index 1 etc. @@ -339,7 +342,8 @@ public void testForeignKeyDeleteCascadeConstraints_forDeletions() { .to("singerName") .build(); - final String concertInsertStatement = getInsertStatementForConcertsTable(); + final String concertInsertStatement = + "INSERT INTO Concert (venue_id, singer_id) VALUES (" + generateQueryParameters(2) + ")"; final Statement concertInsertStatementWithValues = Statement.newBuilder(concertInsertStatement) // Use 'p1' to bind to the parameter with index 1 etc. @@ -361,9 +365,8 @@ public void testForeignKeyDeleteCascadeConstraints_forDeletions() { }); // execute delete - final String singerDeleteStatement = getDeleteStatementForSingerTable(); final Statement singerDeleteStatementWithValues = - Statement.newBuilder(singerDeleteStatement) + Statement.newBuilder("DELETE FROM Singer WHERE singer_id = " + generateQueryParameters(1)) // Use 'p1' to bind to the parameter with index 1 etc. .bind("p1") .to(3L) @@ -380,14 +383,14 @@ public void testForeignKeyDeleteCascadeConstraints_forDeletions() { databaseClient .singleUse() .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_SINGER))) { - assertThat(resultSet.next()).isFalse(); + assertFalse(resultSet.next()); } try (ResultSet resultSet = databaseClient .singleUse() - .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_CONCERTS))) { - assertThat(resultSet.next()).isFalse(); + .executeQuery(Statement.of("SELECT * FROM " + TABLE_NAME_CONCERT))) { + assertFalse(resultSet.next()); } } @@ -412,15 +415,15 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictDueToP transaction.buffer( Arrays.asList( Mutation.newInsertBuilder("Singer") - .set(getSingerIdColumnName()) + .set("singer_id") .to(4L) - .set(getSingerFirstNameColumnName()) + .set("first_name") .to("singerName") .build(), Mutation.delete("Singer", Key.of(4L)))); return null; })); - assertThat(ex.getErrorCode()).isEqualTo(ErrorCode.FAILED_PRECONDITION); + assertEquals(ex.getErrorCode(), ErrorCode.FAILED_PRECONDITION); } @Test @@ -433,7 +436,8 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictsDueTo // referencing a foreign key in child table and deleting the referenced key in parent table // within the same mutations are considered conflicting operations. - final String singerInsertStatement = getInsertStatementForSingerTable(); + final String singerInsertStatement = + "INSERT INTO Singer (singer_id, first_name) VALUES (" + generateQueryParameters(2) + ")"; final Statement singerInsertStatementWithValues = Statement.newBuilder(singerInsertStatement) // Use 'p1' to bind to the parameter with index 1 etc. @@ -460,10 +464,10 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictsDueTo transaction -> { transaction.buffer( Arrays.asList( - Mutation.newInsertBuilder("Concerts") - .set(getConcertVenueIdColumnName()) + Mutation.newInsertBuilder("Concert") + .set("first_name") .to(5L) - .set(getSingerIdColumnName()) + .set("singer_id") .to(5L) .build(), Mutation.delete("Singer", Key.of(5L)))); @@ -471,56 +475,6 @@ public void testForeignKeyDeleteCascadeConstraints_forMutations_onConflictsDueTo })); } - private String getReferentialConstraintsQueryStatement() { - if (dialect.dialect == Dialect.POSTGRESQL) { - return "SELECT DELETE_RULE\n" - + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" - + "WHERE CONSTRAINT_NAME ='Fk_Concerts_Singer'"; - } else { - return "SELECT DELETE_RULE\n" - + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" - + "WHERE CONSTRAINT_NAME =\"Fk_Concerts_Singer\""; - } - } - - private String getReferentialConstraintsQueryStatementV2() { - if (dialect.dialect == Dialect.POSTGRESQL) { - return "SELECT DELETE_RULE\n" - + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" - + "WHERE CONSTRAINT_NAME ='Fk_Concerts_Singer_V2'"; - } else { - return "SELECT DELETE_RULE\n" - + "FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS\n" - + "WHERE CONSTRAINT_NAME =\"Fk_Concerts_Singer_V2\""; - } - } - - private List getCreateAndAlterTableStatementsWithForeignKey() { - if (dialect.dialect == Dialect.POSTGRESQL) { - return ImmutableList.of( - POSTGRES_CREATE_TABLE_SINGER, - POSTGRES_CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY, - POSTGRES_ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY); - } else { - return ImmutableList.of( - CREATE_TABLE_SINGER, - CREATE_TABLE_CONCERT_V2_WITHOUT_FOREIGN_KEY, - ALTER_TABLE_CONCERT_V2_WITH_FOREIGN_KEY); - } - } - - private List getAlterDropForeignKeyDeleteCascadeStatements() { - if (dialect.dialect == Dialect.POSTGRESQL) { - return ImmutableList.of( - ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT, - POSTGRES_ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE); - } else { - return ImmutableList.of( - ALTER_TABLE_CONCERT_V2_DROP_FOREIGN_KEY_CONSTRAINT, - ALTER_TABLE_CONCERT_V2_UPDATE_FOREIGN_KEY_WITHOUT_DELETE_CASCADE); - } - } - private DatabaseAdminClient getDatabaseAdminClient() { return env.getTestHelper().getClient().getDatabaseAdminClient(); } @@ -532,51 +486,29 @@ private DatabaseClient getCreatedDatabaseClient() { return env.getTestHelper().getDatabaseClient(this.GOOGLE_STANDARD_SQL_DATABASE); } - private String getInsertStatementForSingerTable() { - if (dialect.dialect == Dialect.POSTGRESQL) { - return "INSERT INTO Singer (singer_id, first_name) VALUES ($1, $2)"; - } else { - return "INSERT INTO Singer (SingerId, FirstName) VALUES (@p1, @p2)"; - } - } - - private String getInsertStatementForConcertsTable() { - if (dialect.dialect == Dialect.POSTGRESQL) { - return "INSERT INTO Concerts (venue_id, singer_id) VALUES ($1, $2)"; - } else { - return "INSERT INTO Concerts (VenueId, SingerId) VALUES (@p1, @p2)"; - } - } - - private String getDeleteStatementForSingerTable() { + /** + * Returns '@p1, @p2, ..., @pNumParams' for GoogleSQL and $1, $2, ..., $NumParams' for PostgreSQL + * + * @param numParams + * @return + */ + private String generateQueryParameters(final int numParams) { + final List params; if (dialect.dialect == Dialect.POSTGRESQL) { - return "DELETE FROM Singer WHERE singer_id = $1"; - } else { - return "DELETE FROM Singer WHERE SingerId = @p1"; - } - } + params = + IntStream.range(1, numParams + 1) + .mapToObj(paramIndex -> "$" + paramIndex) + .collect(Collectors.toList()); - private String getConcertVenueIdColumnName() { - if (dialect.dialect == Dialect.POSTGRESQL) { - return "venue_id"; } else { - return "VenueId"; + params = + IntStream.range(1, numParams + 1) + .mapToObj(paramIndex -> "@p" + paramIndex) + .collect(Collectors.toList()); } - } - - private String getSingerFirstNameColumnName() { - if (dialect.dialect == Dialect.POSTGRESQL) { - return "first_name"; - } else { - return "FirstName"; - } - } - - private String getSingerIdColumnName() { - if (dialect.dialect == Dialect.POSTGRESQL) { - return "singer_id"; - } else { - return "SingerId"; + if (params.size() == 1) { + return params.get(0); } + return String.join(",", params); } } From cb7c547289c4637f04107ac5f5a8bf89b53750a5 Mon Sep 17 00:00:00 2001 From: Arpan Mishra Date: Mon, 17 Jul 2023 17:20:52 +0530 Subject: [PATCH 11/11] fix:PR comments. --- .../spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java index 53aa5212fa1..f7194247585 100644 --- a/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java +++ b/samples/snippets/src/test/java/com/example/spanner/AlterTableWithForeignKeyDeleteCascadeSampleIT.java @@ -44,7 +44,7 @@ public void testAlterTableWithForeignKeyDeleteCascade() throws Exception { + " CustomerName STRING(62) NOT NULL,\n" + " CONSTRAINT FKShoppingCartsCustomerId" + " FOREIGN KEY (CustomerId)\n" - + " REFERENCES Customers (CustomerId) ON DELETE CASCADE\n" + + " REFERENCES Customers (CustomerId)\n" + " ) PRIMARY KEY (CartId)\n")) .get(5, TimeUnit.MINUTES);