backend/schema/updater/schema_updater_tests/sequence.cc (2,232 lines of code) (raw):

// // 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. // #include "backend/schema/catalog/sequence.h" #include <memory> #include <string> #include <vector> #include "google/spanner/admin/database/v1/common.pb.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "zetasql/base/testing/status_matchers.h" #include "tests/common/proto_matchers.h" #include "absl/status/status.h" #include "backend/schema/catalog/column.h" #include "backend/schema/catalog/schema.h" #include "backend/schema/catalog/view.h" #include "backend/schema/graph/schema_node.h" #include "backend/schema/updater/schema_updater_tests/base.h" #include "common/errors.h" #include "tests/common/scoped_feature_flags_setter.h" namespace google { namespace spanner { namespace emulator { namespace backend { namespace test { // For the following tests, a custom PG DDL statement is required as translating // expressions from GSQL to PG is not supported in tests. using database_api::DatabaseDialect::GOOGLE_STANDARD_SQL; using database_api::DatabaseDialect::POSTGRESQL; using ::google::spanner::emulator::test::ScopedEmulatorFeatureFlagsSetter; using testing::Optional; class SequenceSchemaUpdaterTest : public SchemaUpdaterTest { public: SequenceSchemaUpdaterTest() : flag_setter_({.enable_identity_columns = true}) {} const ScopedEmulatorFeatureFlagsSetter flag_setter_; }; INSTANTIATE_TEST_SUITE_P( SchemaUpdaterPerDialectTests, SequenceSchemaUpdaterTest, testing::Values(GOOGLE_STANDARD_SQL, POSTGRESQL), [](const testing::TestParamInfo<SequenceSchemaUpdaterTest::ParamType>& info) { return database_api::DatabaseDialect_Name(info.param); }); TEST_P(SequenceSchemaUpdaterTest, SequenceNotSupportedWhenFlagIsOff) { if (GetParam() == GOOGLE_STANDARD_SQL) { GTEST_SKIP(); } ScopedEmulatorFeatureFlagsSetter setter( {.enable_bit_reversed_positive_sequences_postgresql = false}); EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SequenceNotSupportedInPostgreSQL())); EXPECT_THAT(CreateSchema({R"( ALTER SEQUENCE myseq RESTART COUNTER 1 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SequenceNotSupportedInPostgreSQL())); EXPECT_THAT(CreateSchema({R"( DROP SEQUENCE myseq )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SequenceNotSupportedInPostgreSQL())); } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_Basic) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); if (GetParam() == POSTGRESQL) { EXPECT_EQ(sequence->start_with_counter().value(), 1); EXPECT_EQ(sequence->DebugString(), R"(Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE start_with_counter: 1)"); } else { EXPECT_FALSE(sequence->start_with_counter().has_value()); EXPECT_EQ(sequence->DebugString(), "Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE"); } EXPECT_FALSE(sequence->skip_range_min().has_value()); EXPECT_FALSE(sequence->skip_range_max().has_value()); } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_BasicWithDefaultValue) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( int64_col bigint DEFAULT nextval('myseq') PRIMARY KEY, value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( int64_col INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq)), value INT64 ) PRIMARY KEY (int64_col) )", })); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); if (GetParam() == POSTGRESQL) { EXPECT_EQ(sequence->start_with_counter().value(), 1); } else { EXPECT_FALSE(sequence->start_with_counter().has_value()); } EXPECT_FALSE(sequence->skip_range_min().has_value()); EXPECT_FALSE(sequence->skip_range_max().has_value()); } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_AllOptions) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE 1 1000 START COUNTER 5000; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", start_with_counter = 5000, skip_range_min = 1, skip_range_max = 1000 ) )"})); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter().value(), 5000); EXPECT_EQ(sequence->skip_range_min().value(), 1); EXPECT_EQ(sequence->skip_range_max().value(), 1000); EXPECT_EQ(sequence->DebugString(), R"(Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE start_with_counter: 5000 skipped range: [1, 1000])"); } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_NullOptions) { // PostgreSQL doesn't accept NULL in sequence SQL clause. if (GetParam() == POSTGRESQL) GTEST_SKIP(); ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr<const Schema> schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_min = NULL, skip_range_max = NULL, start_with_counter = NULL ) )"})); const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_FALSE(sequence->start_with_counter().has_value()); EXPECT_FALSE(sequence->skip_range_min().has_value()); EXPECT_FALSE(sequence->skip_range_max().has_value()); } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_OneSkipRangeValueSet) { // PostgreSQL doesn't accept NULL or only 1 value in the `SKIP RANGE` clause. if (GetParam() == POSTGRESQL) GTEST_SKIP(); EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_min = NULL, skip_range_max = 1000 ) )"}), StatusIs(error::SequenceSkipRangeMinMaxNotSetTogether())); EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_min = 1, skip_range_max = NULL ) )"}), StatusIs(error::SequenceSkipRangeMinMaxNotSetTogether())); EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_max = 1000 ) )"}), StatusIs(error::SequenceSkipRangeMinMaxNotSetTogether())); EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_min = 1 ) )"}), StatusIs(error::SequenceSkipRangeMinMaxNotSetTogether())); } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_NegativeStartWithCounter) { if (GetParam() == POSTGRESQL) { EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE START COUNTER -1 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::InvalidSequenceStartWithCounterValue())); } else { EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", start_with_counter = -1 ) )"}), StatusIs(error::InvalidSequenceStartWithCounterValue())); } } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_NegativeSkippedRange) { if (GetParam() == POSTGRESQL) { EXPECT_THAT( CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE -100 -1 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SequenceSkippedRangeHasAtleastOnePositiveNumber())); } else { EXPECT_THAT( CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_min = -100, skip_range_max = -1 ) )"}), StatusIs(error::SequenceSkippedRangeHasAtleastOnePositiveNumber())); } } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_InvaliSkippedRange) { if (GetParam() == POSTGRESQL) { EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE 100 1 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SequenceSkipRangeMinLargerThanMax())); } else { EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_min = 100, skip_range_max = 1 ) )"}), StatusIs(error::SequenceSkipRangeMinLargerThanMax())); } } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_DuplicateSequenceGivesError) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } // Create a sequence with the same name. Expect error. if (GetParam() == POSTGRESQL) { EXPECT_THAT( UpdateSchema(schema.get(), {R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE 1 1000 START COUNTER 500 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SchemaObjectAlreadyExists("Sequence", "myseq"))); } else { EXPECT_THAT( UpdateSchema(schema.get(), {R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", start_with_counter = 500, skip_range_min = 1, skip_range_max = 1000 ) )"}), StatusIs(error::SchemaObjectAlreadyExists("Sequence", "myseq"))); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); if (GetParam() == POSTGRESQL) { EXPECT_EQ(sequence->start_with_counter().value(), 1); } else { EXPECT_FALSE(sequence->start_with_counter().has_value()); } EXPECT_FALSE(sequence->skip_range_min().has_value()); EXPECT_FALSE(sequence->skip_range_max().has_value()); } TEST_P(SequenceSchemaUpdaterTest, CreateSequence_DuplicateSequenceWithIfNotExists) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } const Sequence* sequence = schema->FindSequence("myseq"); std::string sequence_id = sequence->id(); // Create a sequence with the same name, but we use the IF NOT EXISTS clause // here, so the statement succeeds. But the new sequence should not overwrite // the existing one. if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE SEQUENCE IF NOT EXISTS myseq BIT_REVERSED_POSITIVE SKIP RANGE 1 1000 START COUNTER 500 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE SEQUENCE IF NOT EXISTS myseq OPTIONS ( sequence_kind = "bit_reversed_positive", start_with_counter = 500, skip_range_min = 1, skip_range_max = 1000 ) )"})); } // Indicate that the DDL statement is a no-op. EXPECT_EQ(schema, nullptr); } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_AlterNonExistsSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER SEQUENCE nonexist SKIP RANGE 1 1000 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SequenceNotFound("nonexist"))); } else { EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER SEQUENCE nonexist SET OPTIONS ( skip_range_min = 1, skip_range_max = 1000 ) )"}), StatusIs(error::SequenceNotFound("nonexist"))); } } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_WithIfExists) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE IF EXISTS nonexist SKIP RANGE 1 1000 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE IF EXISTS nonexist SET OPTIONS ( skip_range_min = 1, skip_range_max = 1000 ) )"})); } } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_SetAllOptions) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SKIP RANGE 1 1000 RESTART COUNTER 5000; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( sequence_kind = "bit_reversed_positive", start_with_counter = 5000, skip_range_min = 1, skip_range_max = 1000 ) )"})); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter(), 5000); EXPECT_EQ(sequence->skip_range_min().value(), 1); EXPECT_EQ(sequence->skip_range_max().value(), 1000); EXPECT_EQ(sequence->DebugString(), "Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE\n " "start_with_counter: 5000\n skipped range: [1, 1000]"); } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_ChangeStartWithCounter) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); if (GetParam() == POSTGRESQL) { EXPECT_EQ(sequence->start_with_counter().value(), 1); } else { EXPECT_FALSE(sequence->start_with_counter().has_value()); } EXPECT_FALSE(sequence->skip_range_min().has_value()); EXPECT_FALSE(sequence->skip_range_max().has_value()); if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq RESTART COUNTER 5000; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( start_with_counter = 5000 ) )"})); } sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter(), 5000); EXPECT_FALSE(sequence->skip_range_min().has_value()); EXPECT_FALSE(sequence->skip_range_max().has_value()); } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_ChangeAllOptions) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE 1 1000 START COUNTER 2000 ; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", start_with_counter = 2000, skip_range_min = 1, skip_range_max = 1000 ) )"})); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter(), 2000); EXPECT_EQ(sequence->skip_range_min().value(), 1); EXPECT_EQ(sequence->skip_range_max().value(), 1000); if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SKIP RANGE 1000 10000 RESTART COUNTER 5000; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( start_with_counter = 5000, skip_range_min = 1000, skip_range_max = 10000 ) )"})); } sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter(), 5000); EXPECT_EQ(sequence->skip_range_min().value(), 1000); EXPECT_EQ(sequence->skip_range_max().value(), 10000); } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_SetAllOptionsChangeStartWithCounter) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE 1 1000 START COUNTER 2000 ; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", start_with_counter = 2000, skip_range_min = 1, skip_range_max = 1000 ) )"})); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter(), 2000); EXPECT_EQ(sequence->skip_range_min().value(), 1); EXPECT_EQ(sequence->skip_range_max().value(), 1000); if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq RESTART COUNTER 3456; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( start_with_counter = 3456 ) )"})); } sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter(), 3456); EXPECT_EQ(sequence->skip_range_min().value(), 1); EXPECT_EQ(sequence->skip_range_max().value(), 1000); } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_ClearAllOptions) { // PostgreSQL doesn't accept NULL in the `SKIP RANGE` clause. if (GetParam() == POSTGRESQL) GTEST_SKIP(); std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE 1 1000 START COUNTER 2000 ; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", start_with_counter = 2000, skip_range_min = 1, skip_range_max = 1000 ) )"})); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter(), 2000); EXPECT_EQ(sequence->skip_range_min().value(), 1); EXPECT_EQ(sequence->skip_range_max().value(), 1000); ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( skip_range_min = NULL, skip_range_max = NULL, start_with_counter = NULL ) )"})); sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_FALSE(sequence->start_with_counter().has_value()); EXPECT_FALSE(sequence->skip_range_min().has_value()); EXPECT_FALSE(sequence->skip_range_max().has_value()); } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_ChangeOneSkipRangeValue) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE 1 1000 START COUNTER 2000 ; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", start_with_counter = 2000, skip_range_min = 1, skip_range_max = 1000 ) )"})); } const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter(), 2000); EXPECT_EQ(sequence->skip_range_min().value(), 1); EXPECT_EQ(sequence->skip_range_max().value(), 1000); if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SKIP RANGE 1 10000; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( skip_range_max = 10000 ) )"})); } sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->start_with_counter(), 2000); EXPECT_EQ(sequence->skip_range_min().value(), 1); EXPECT_EQ(sequence->skip_range_max().value(), 10000); } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_OneSkipRangeValueSetToNull) { // PostgreSQL doesn't accept NULL or only 1 value in the `SKIP RANGE` clause. if (GetParam() == POSTGRESQL) GTEST_SKIP(); ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr<const Schema> schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_min = 1, skip_range_max = 1000 ) )"})); EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_min = NULL, skip_range_max = 1000 ) )"}), StatusIs(error::SequenceSkipRangeMinMaxNotSetTogether())); EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( sequence_kind = "bit_reversed_positive", skip_range_min = 1, skip_range_max = NULL ) )"}), StatusIs(error::SequenceSkipRangeMinMaxNotSetTogether())); } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_NegativeStartWithCounter) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq RESTART COUNTER -1 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::InvalidSequenceStartWithCounterValue())); } else { EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( start_with_counter = -1 ) )"}), StatusIs(error::InvalidSequenceStartWithCounterValue())); } } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_NegativeSkippedRange) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { EXPECT_THAT( UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SKIP RANGE -100 -1 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SequenceSkippedRangeHasAtleastOnePositiveNumber())); } else { EXPECT_THAT( UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( skip_range_min = -100, skip_range_max = -1 ) )"}), StatusIs(error::SequenceSkippedRangeHasAtleastOnePositiveNumber())); } } TEST_P(SequenceSchemaUpdaterTest, AlterSequence_InvaliSkippedRange) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SKIP RANGE 100 1 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SequenceSkipRangeMinLargerThanMax())); } else { EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( skip_range_min = 100, skip_range_max = 1 ) )"}), StatusIs(error::SequenceSkipRangeMinLargerThanMax())); } } TEST_P(SequenceSchemaUpdaterTest, DropSequence_Basic) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"})); } EXPECT_EQ(schema->FindSequence("myseq"), nullptr); } TEST_P(SequenceSchemaUpdaterTest, DropSequence_DropAndCreateAgain) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"})); } EXPECT_EQ(schema->FindSequence("myseq"), nullptr); if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } EXPECT_NE(schema->FindSequence("myseq"), nullptr); } TEST_P(SequenceSchemaUpdaterTest, DropSequence_DropNonExistSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { EXPECT_THAT(UpdateSchema(schema.get(), {R"( DROP SEQUENCE notmyseq )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), StatusIs(error::SequenceNotFound("notmyseq"))); } else { EXPECT_THAT(UpdateSchema(schema.get(), {R"( DROP SEQUENCE notmyseq )"}), StatusIs(error::SequenceNotFound("notmyseq"))); } EXPECT_NE(schema->FindSequence("myseq"), nullptr); } TEST_P(SequenceSchemaUpdaterTest, DropSequence_DropNonExistSequenceWithIfExists) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE IF NOT EXISTS myseq BIT_REVERSED_POSITIVE; )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( DROP SEQUENCE IF EXISTS notmyseq )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( DROP SEQUENCE IF EXISTS notmyseq )"})); } EXPECT_NE(schema->FindSequence("myseq"), nullptr); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_CreateAndDropColumn) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( id bigint PRIMARY KEY, int64_col bigint DEFAULT nextval('myseq'), value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( id INT64, int64_col INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq)), value INT64 ) PRIMARY KEY (id) )", })); } // Ensure dependencies are set up correctly const Column* int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_THAT(int64_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); // Try to drop the sequence, receive error because a column is using it. EXPECT_THAT(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), StatusIs(error::InvalidDropDependentColumn( "SEQUENCE", "myseq", "test_table.int64_col"))); // Drop the column, dependencies are dropped accordingly. ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table DROP COLUMN int64_col )"})); int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_EQ(int64_col, nullptr); // Now we can drop the sequence. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_AlterSequenceDoesNotAffectColumnDependency) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( id bigint PRIMARY KEY, int64_col bigint DEFAULT nextval('myseq'), value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( id INT64, int64_col INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq)), value INT64 ) PRIMARY KEY (id) )", })); } const Column* int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_THAT(int64_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); // Alter the sequence if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq RESTART COUNTER 100 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( start_with_counter = 100 ) )"})); } // Try to drop the sequence, receive error because column int64_col is using // it. EXPECT_THAT(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), StatusIs(error::InvalidDropDependentColumn( "SEQUENCE", "myseq", "test_table.int64_col"))); // Drop the column default value, dependencies are dropped accordingly. ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN int64_col DROP DEFAULT )"})); int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_TRUE(int64_col->sequences_used().empty()); // Now we can drop the sequence. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_CreateAndDropColumnDefaultValue) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( int64_col bigint DEFAULT 1 + 10 +nextval('myseq') PRIMARY KEY, value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( int64_col INT64 DEFAULT (1 + 10 + GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq)), value INT64 ) PRIMARY KEY (int64_col) )", })); } // Ensure dependencies are set up correctly const Column* int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_THAT(int64_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); // Try to drop the sequence, receive error because a column is using it. EXPECT_THAT(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), StatusIs(error::InvalidDropDependentColumn( "SEQUENCE", "myseq", "test_table.int64_col"))); // Drop the column default value, dependencies are dropped accordingly. ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN int64_col DROP DEFAULT )"})); int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_TRUE(int64_col->sequences_used().empty()); // Now we can drop the sequence. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_TwoColumnDefaultValues) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( int64_col bigint DEFAULT nextval('myseq') PRIMARY KEY, second_col bigint DEFAULT nextval('myseq'), value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( int64_col INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq)), second_col INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq)), value INT64 ) PRIMARY KEY (int64_col) )", })); } // Ensure dependencies are set up correctly const Column* int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_THAT(int64_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); const Column* second_col = schema->FindTable("test_table")->FindColumn("second_col"); EXPECT_NE(second_col, nullptr); EXPECT_THAT(second_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); // Try to drop the sequence, receive error because a column is using it. EXPECT_THAT(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), StatusIs(error::InvalidDropDependentColumn( "SEQUENCE", "myseq", "test_table.int64_col"))); // Drop one column default value, only one dependency is dropped. ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN int64_col DROP DEFAULT )"})); int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_TRUE(int64_col->sequences_used().empty()); second_col = schema->FindTable("test_table")->FindColumn("second_col"); EXPECT_NE(second_col, nullptr); EXPECT_THAT(second_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); // Drop the other column default value, now there is no dependency left. ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN second_col DROP DEFAULT )"})); int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_TRUE(int64_col->sequences_used().empty()); second_col = schema->FindTable("test_table")->FindColumn("second_col"); EXPECT_NE(second_col, nullptr); EXPECT_TRUE(second_col->sequences_used().empty()); // Now we can drop the sequence. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_TwoSequencesInOneColumnDefaultValue) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE SEQUENCE myseq2 BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( int64_col bigint DEFAULT nextval('myseq') + nextval('myseq2') PRIMARY KEY, value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE SEQUENCE myseq2 OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( int64_col INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq) + GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq2)), value INT64 ) PRIMARY KEY (int64_col) )", })); } // Ensure dependencies are set up correctly const Column* int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_THAT( int64_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq"), schema->FindSequence("myseq2")})); // Try to drop the sequences, receive error because a column is using it. EXPECT_THAT(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), StatusIs(error::InvalidDropDependentColumn( "SEQUENCE", "myseq", "test_table.int64_col"))); EXPECT_THAT(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq2 )"}), StatusIs(error::InvalidDropDependentColumn( "SEQUENCE", "myseq2", "test_table.int64_col"))); // Drop the column default value, corresponding dependencies are now dropped. ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN int64_col DROP DEFAULT )"})); int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_TRUE(int64_col->sequences_used().empty()); // Now we can drop the sequences. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq2)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_AlterColumnToUseSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( int64_col bigint PRIMARY KEY, value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( int64_col INT64, value INT64 ) PRIMARY KEY (int64_col) )", })); } // There is no dependency registered const Column* int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_TRUE(int64_col->sequences_used().empty()); // Alter column to set default value that uses the sequence if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN int64_col SET DEFAULT nextval('myseq') )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN int64_col SET DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq)) )"})); } // Ensure dependencies are set up correctly int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_THAT(int64_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_AlterColumnToAddMoreSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE SEQUENCE myseq2 BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( int64_col bigint PRIMARY KEY DEFAULT nextval('myseq'), value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE SEQUENCE myseq2 OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( int64_col INT64 DEFAULT ( GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq)), value INT64 ) PRIMARY KEY (int64_col) )", })); } // `int64_col` has one dependency on `myseq` const Column* int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_THAT(int64_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); // Alter column to add one more dependency on myseq2. if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN int64_col SET DEFAULT nextval('myseq') + nextval('myseq2') )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN int64_col SET DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq) + GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq2)) )"})); } // `int64_col` now has two dependencies on `myseq` and `myseq2`. int64_col = schema->FindTable("test_table")->FindColumn("int64_col"); EXPECT_NE(int64_col, nullptr); EXPECT_THAT( int64_col->sequences_used(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq"), schema->FindSequence("myseq2")})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_NewColumnUsesNonExistSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { EXPECT_THAT(UpdateSchema(schema.get(), {R"( CREATE TABLE test_table ( int64_col bigint DEFAULT nextval('nonexist') PRIMARY KEY, value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr("Sequence not found: nonexist"))); } else { EXPECT_THAT(UpdateSchema(schema.get(), {R"( CREATE TABLE test_table ( int64_col INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE nonexist)), value INT64 ) PRIMARY KEY (int64_col) )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr("Sequence not found: nonexist"))); } } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_AlterColumnUsesNonExistSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( int64_col bigint DEFAULT nextval('myseq') PRIMARY KEY, second_col bigint, value bigint ) )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( int64_col INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE myseq)), second_col INT64, value INT64 ) PRIMARY KEY (int64_col) )", })); } // Alter column SET DEFAULT should fail: if (GetParam() == POSTGRESQL) { EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN second_col SET DEFAULT nextval('nonexist') )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr("Sequence not found: nonexist"))); } else { EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN second_col SET DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE nonexist)) )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr("Sequence not found: nonexist"))); } // Alter column set the whole column definition should fail. This syntax is // only in ZetaSQL. if (GetParam() == GOOGLE_STANDARD_SQL) { EXPECT_THAT(UpdateSchema(schema.get(), {R"( ALTER TABLE test_table ALTER COLUMN second_col INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE nonexist)) )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr("Sequence not found: nonexist"))); } } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_CreateAndDropView) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE VIEW myview SQL SECURITY INVOKER AS SELECT spanner.get_internal_sequence_state('myseq') AS myseq_state )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE VIEW myview SQL SECURITY INVOKER AS SELECT get_internal_sequence_state(SEQUENCE myseq) AS myseq_state )", })); } // Ensure dependencies are set up correctly const View* myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_THAT(myview->dependencies(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); // Try to drop the sequence, receive error because a view is using it. EXPECT_THAT( UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kFailedPrecondition, testing::HasSubstr("Cannot drop SEQUENCE `myseq` on which there " "are dependent views"))); // Drop the view, dependencies are dropped accordingly. ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( DROP VIEW myview )"})); EXPECT_EQ(schema->FindView("myview"), nullptr); // Now we can drop the sequence. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_CreateViewQueryTable) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE TABLE test_table ( int64_col bigint PRIMARY KEY, value bigint ) )", R"( CREATE VIEW myview SQL SECURITY INVOKER AS SELECT spanner.get_internal_sequence_state('myseq') AS myseq_state, t.int64_col AS col FROM test_table t )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE TABLE test_table ( int64_col INT64, value INT64 ) PRIMARY KEY (int64_col) )", R"( CREATE VIEW myview SQL SECURITY INVOKER AS SELECT get_internal_sequence_state(SEQUENCE myseq) AS myseq_state, t.int64_col AS col FROM test_table t )", })); } // Ensure dependencies are set up correctly const View* myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_THAT( myview->dependencies(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq"), schema->FindTable("test_table"), schema->FindTable("test_table")->FindColumn("int64_col")})); // Try to drop the sequence, receive error because a view is using it. EXPECT_THAT( UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kFailedPrecondition, testing::HasSubstr("Cannot drop SEQUENCE `myseq` on which there " "are dependent views"))); // Drop the view, dependencies are dropped accordingly. ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( DROP VIEW myview )"})); EXPECT_EQ(schema->FindView("myview"), nullptr); // Now we can drop the sequence. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_CreateAndReplaceView) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )", R"( CREATE VIEW myview SQL SECURITY INVOKER AS SELECT spanner.get_internal_sequence_state('myseq') AS myseq_state )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE VIEW myview SQL SECURITY INVOKER AS SELECT get_internal_sequence_state(SEQUENCE myseq) AS myseq_state )", })); } // Ensure dependencies are set up correctly const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); const View* myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_THAT(myview->dependencies(), testing::UnorderedElementsAreArray( (std::vector<const SchemaNode*>{sequence}))); // Try to drop the sequence, receive error because a column is using it. EXPECT_THAT( UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kFailedPrecondition, testing::HasSubstr("Cannot drop SEQUENCE `myseq` on which there " "are dependent views"))); // Replace the view to not use sequence, dependencies are dropped accordingly. if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )"})); } sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_TRUE(myview->dependencies().empty()); // Now we can drop the sequence. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_AlterSequenceDoesNotAffectViewDependency) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )", R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT spanner.get_internal_sequence_state('myseq') AS myseq_state )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT get_internal_sequence_state(SEQUENCE myseq) AS myseq_state )", })); } // Ensure dependencies are set up correctly const View* myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_THAT( myview->dependencies(), testing::UnorderedElementsAreArray( (std::vector<const SchemaNode*>{schema->FindSequence("myseq")}))); // Alter the sequence if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq RESTART COUNTER 100 )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS ( start_with_counter = 100 ) )"})); } // Try to drop the sequence, receive error because a view is using it. EXPECT_THAT( UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kFailedPrecondition, testing::HasSubstr("Cannot drop SEQUENCE `myseq` on which there " "are dependent views"))); // Replace the view to not use sequence, dependencies are dropped accordingly. if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )"})); } myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_TRUE(myview->dependencies().empty()); // Now we can drop the sequence. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_TwoViewsUseOneSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT spanner.get_internal_sequence_state('myseq') AS myseq_state )", R"( CREATE OR REPLACE VIEW myview2 SQL SECURITY INVOKER AS SELECT spanner.get_internal_sequence_state('myseq') AS myseq_state )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT get_internal_sequence_state(SEQUENCE myseq) AS myseq_state )", R"( CREATE OR REPLACE VIEW myview2 SQL SECURITY INVOKER AS SELECT get_internal_sequence_state(SEQUENCE myseq) AS myseq_state )"})); } // Ensure dependencies are set up correctly const View* myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_THAT( myview->dependencies(), testing::UnorderedElementsAreArray( (std::vector<const SchemaNode*>{schema->FindSequence("myseq")}))); const View* myview2 = schema->FindView("myview2"); EXPECT_NE(myview2, nullptr); EXPECT_THAT( myview2->dependencies(), testing::UnorderedElementsAreArray( (std::vector<const SchemaNode*>{schema->FindSequence("myseq")}))); // Try to drop the sequence, receive error because a view is using it. EXPECT_THAT( UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kFailedPrecondition, testing::HasSubstr("Cannot drop SEQUENCE `myseq` on which there " "are dependent views"))); // Replace one view to not use sequence, one dependency is dropped. if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )"})); } myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_TRUE(myview->dependencies().empty()); myview2 = schema->FindView("myview2"); EXPECT_NE(myview2, nullptr); EXPECT_THAT( myview2->dependencies(), testing::UnorderedElementsAreArray( (std::vector<const SchemaNode*>{schema->FindSequence("myseq")}))); // Drop the other view, dependencies are dropped accordingly. ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( DROP VIEW myview2 )"})); EXPECT_EQ(schema->FindView("myview2"), nullptr); // Now we can drop the sequence. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_TwoSequencesInOneView) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE SEQUENCE myseq2 BIT_REVERSED_POSITIVE; )", R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT spanner.get_internal_sequence_state('myseq') AS myseq_state, spanner.get_internal_sequence_state('myseq2') AS myseq2_state )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE SEQUENCE myseq2 OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT get_internal_sequence_state(SEQUENCE myseq) AS myseq_state, get_internal_sequence_state(SEQUENCE myseq2) AS myseq2_state )", })); } // Ensure dependencies are set up correctly const View* myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_THAT( myview->dependencies(), testing::UnorderedElementsAreArray((std::vector<const SchemaNode*>{ schema->FindSequence("myseq"), schema->FindSequence("myseq2")}))); // Try to drop the sequences, receive error. EXPECT_THAT( UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kFailedPrecondition, testing::HasSubstr("Cannot drop SEQUENCE `myseq` on which there " "are dependent views"))); EXPECT_THAT( UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq2 )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kFailedPrecondition, testing::HasSubstr("Cannot drop SEQUENCE `myseq2` on which there " "are dependent views"))); // Replace the view to not use sequences, corresponding dependencies are now // dropped. if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )"})); } myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_TRUE(myview->dependencies().empty()); // Now we can drop the sequences. ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq)"})); ZETASQL_EXPECT_OK(UpdateSchema(schema.get(), {R"( DROP SEQUENCE myseq2)"})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_ReplaceViewToUseSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE; )", R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )", R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT 1 AS one )", })); } // There is no dependency registered const View* myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_TRUE(myview->dependencies().empty()); // Replace view to use the sequence if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT spanner.get_internal_sequence_state('myseq') AS myseq_state )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT get_internal_sequence_state(SEQUENCE myseq) AS myseq_state, )"})); } // Ensure dependencies are set up correctly myview = schema->FindView("myview"); EXPECT_NE(myview, nullptr); EXPECT_THAT(myview->dependencies(), testing::UnorderedElementsAreArray(std::vector<const SchemaNode*>{ schema->FindSequence("myseq")})); } TEST_P(SequenceSchemaUpdaterTest, SequenceDependency_ViewUsesNonExistSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false)); } else { ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq OPTIONS ( sequence_kind = "bit_reversed_positive" ) )"})); } if (GetParam() == POSTGRESQL) { EXPECT_THAT(UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT spanner.get_internal_sequence_state('nonexist') AS myseq_state )"}, /*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL, /*use_gsql_to_pg_translation=*/false), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr("Sequence not found: nonexist"))); } else { EXPECT_THAT(UpdateSchema(schema.get(), {R"( CREATE OR REPLACE VIEW myview SQL SECURITY INVOKER AS SELECT get_internal_sequence_state(SEQUENCE nonexist) AS myseq_state )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr("Sequence not found: nonexist"))); } } TEST_P(SequenceSchemaUpdaterTest, GSQLSequenceClauseNotSupportedWhenFlagIsOff) { if (GetParam() == POSTGRESQL) { GTEST_SKIP(); } ScopedEmulatorFeatureFlagsSetter setter({.enable_identity_columns = false}); EXPECT_THAT(CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr( "Using SQL clauses to configure sequence options is not " "supported in CREATE SEQUENCE statements"))); EXPECT_THAT(CreateSchema({R"( ALTER SEQUENCE myseq RESTART COUNTER WITH 1 )"}), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr("RESTART COUNTER WITH is not supported in " "ALTER SEQUENCE statements"))); } TEST_P(SequenceSchemaUpdaterTest, GSQLSequenceClause_CreateSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { GTEST_SKIP(); } ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE 1000, 2000 START COUNTER WITH 100 )"})); const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_TRUE(sequence->start_with_counter().has_value()); EXPECT_EQ(sequence->DebugString(), "Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE\n " "start_with_counter: 100\n skipped range: [1000, 2000]"); EXPECT_THAT(sequence->start_with_counter(), Optional(100)); EXPECT_THAT(sequence->skip_range_min(), Optional(1000)); EXPECT_THAT(sequence->skip_range_max(), Optional(2000)); } TEST_P(SequenceSchemaUpdaterTest, GSQLSequenceClause_SpecifyNoSequenceKindFailed) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { GTEST_SKIP(); } EXPECT_THAT(CreateSchema({"CREATE SEQUENCE myseq"}), zetasql_base::testing::StatusIs( absl::StatusCode::kInvalidArgument, testing::HasSubstr( "The sequence does not have a valid sequence kind. " "Please specify the sequence kind explicitly or set " "the database option `default_sequence_kind`."))); } TEST_P(SequenceSchemaUpdaterTest, GSQLSequenceClause_SpecifyNoSequenceKindSuccess) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { GTEST_SKIP(); } ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({ R"(ALTER DATABASE db SET OPTIONS ( default_sequence_kind = 'bit_reversed_positive'))", R"( CREATE SEQUENCE myseq SKIP RANGE 1000, 2000 START COUNTER WITH 100 )", R"( CREATE SEQUENCE myseq2 OPTIONS ( start_with_counter = 200, skip_range_min = 3000, skip_range_max = 4000 ) )", R"( CREATE SEQUENCE myseq3 )"})); const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->sequence_kind_name(), "BIT_REVERSED_POSITIVE"); EXPECT_TRUE(sequence->start_with_counter().has_value()); EXPECT_EQ(sequence->DebugString(), "Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE\n " "start_with_counter: 100\n skipped range: [1000, 2000]"); EXPECT_THAT(sequence->start_with_counter(), Optional(100)); EXPECT_THAT(sequence->skip_range_min(), Optional(1000)); EXPECT_THAT(sequence->skip_range_max(), Optional(2000)); const Sequence* sequence2 = schema->FindSequence("myseq2"); EXPECT_NE(sequence2, nullptr); EXPECT_EQ(sequence2->sequence_kind_name(), "BIT_REVERSED_POSITIVE"); EXPECT_TRUE(sequence2->start_with_counter().has_value()); EXPECT_EQ(sequence2->DebugString(), "Sequence myseq2. Sequence kind: BIT_REVERSED_POSITIVE\n " "start_with_counter: 200\n skipped range: [3000, 4000]"); EXPECT_THAT(sequence2->start_with_counter(), Optional(200)); EXPECT_THAT(sequence2->skip_range_min(), Optional(3000)); EXPECT_THAT(sequence2->skip_range_max(), Optional(4000)); const Sequence* sequence3 = schema->FindSequence("myseq3"); EXPECT_NE(sequence3, nullptr); EXPECT_EQ(sequence3->sequence_kind_name(), "BIT_REVERSED_POSITIVE"); EXPECT_EQ(sequence3->DebugString(), "Sequence myseq3. Sequence kind: BIT_REVERSED_POSITIVE"); EXPECT_FALSE(sequence3->start_with_counter().has_value()); EXPECT_FALSE(sequence3->skip_range_min().has_value()); EXPECT_FALSE(sequence3->skip_range_max().has_value()); } TEST_P(SequenceSchemaUpdaterTest, GSQLSequenceClause_UseBothClausesAndOptions_CreateFailed) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { GTEST_SKIP(); } EXPECT_THAT( CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE OPTIONS (sequence_kind = "bit_reversed_positive") )"}), StatusIs(error::CannotSetSequenceClauseAndOptionTogether("myseq"))); EXPECT_THAT( CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE OPTIONS (start_with_counter = 5000) )"}), StatusIs(error::CannotSetSequenceClauseAndOptionTogether("myseq"))); } TEST_P(SequenceSchemaUpdaterTest, GSQLSequenceClause_UseBothClausesAndOptions_AlterFailed) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { GTEST_SKIP(); } ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE )", R"( CREATE SEQUENCE myseq2 OPTIONS (sequence_kind = "bit_reversed_positive") )"})); EXPECT_THAT( UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SET OPTIONS (start_with_counter = 5000) )"}), StatusIs(error::CannotSetSequenceClauseAndOptionTogether("myseq"))); EXPECT_THAT( UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq2 RESTART COUNTER WITH 5000 )"}), StatusIs(error::CannotSetSequenceClauseAndOptionTogether("myseq2"))); } TEST_P(SequenceSchemaUpdaterTest, GSQLSequenceClause_AlterSequence) { std::unique_ptr<const Schema> schema; if (GetParam() == POSTGRESQL) { GTEST_SKIP(); } ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"( CREATE SEQUENCE myseq BIT_REVERSED_POSITIVE SKIP RANGE 1000, 2000 START COUNTER WITH 100 )"})); const Sequence* sequence = schema->FindSequence("myseq"); EXPECT_NE(sequence, nullptr); EXPECT_EQ(sequence->DebugString(), "Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE\n " "start_with_counter: 100\n skipped range: [1000, 2000]"); ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq SKIP RANGE 3000, 4000 )"})); sequence = schema->FindSequence("myseq"); EXPECT_EQ(sequence->DebugString(), "Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE\n " "start_with_counter: 100\n skipped range: [3000, 4000]"); ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq RESTART COUNTER WITH 200 )"})); sequence = schema->FindSequence("myseq"); EXPECT_EQ(sequence->DebugString(), "Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE\n " "start_with_counter: 200\n skipped range: [3000, 4000]"); ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"( ALTER SEQUENCE myseq NO SKIP RANGE )"})); sequence = schema->FindSequence("myseq"); EXPECT_EQ(sequence->DebugString(), "Sequence myseq. Sequence kind: BIT_REVERSED_POSITIVE\n " "start_with_counter: 200"); } } // namespace test } // namespace backend } // namespace emulator } // namespace spanner } // namespace google