backend/schema/updater/schema_updater_tests/base.h (170 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. // #ifndef THIRD_PARTY_CLOUD_SPANNER_EMULATOR_BACKEND_SCHEMA_UPDATER_SCHEMA_UPDATER_TESTS_BASE_H_ #define THIRD_PARTY_CLOUD_SPANNER_EMULATOR_BACKEND_SCHEMA_UPDATER_SCHEMA_UPDATER_TESTS_BASE_H_ #include <memory> #include <string> #include <vector> #include "google/spanner/admin/database/v1/common.pb.h" #include "zetasql/public/type.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/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "backend/common/ids.h" #include "backend/database/pg_oid_assigner/pg_oid_assigner.h" #include "backend/schema/catalog/foreign_key.h" #include "backend/schema/catalog/schema.h" #include "backend/schema/catalog/table.h" #include "backend/schema/graph/schema_graph.h" #include "backend/schema/graph/schema_graph_editor.h" #include "backend/schema/graph/schema_node.h" #include "backend/schema/updater/schema_updater.h" #include "backend/storage/in_memory_storage.h" #include "common/errors.h" #include "common/limits.h" #include "tests/common/scoped_feature_flags_setter.h" #include "third_party/spanner_pg/ddl/spangres_direct_schema_printer_impl.h" #include "third_party/spanner_pg/ddl/spangres_schema_printer.h" namespace google { namespace spanner { namespace emulator { namespace backend { namespace test { using ::google::spanner::emulator::test::ScopedEmulatorFeatureFlagsSetter; using postgres_translator::spangres::CreateSpangresDirectSchemaPrinter; using postgres_translator::spangres::SpangresSchemaPrinter; namespace database_api = ::google::spanner::admin::database::v1; // Matcher for matching an absl::StatusOr with an expected status. MATCHER_P(StatusIs, status, "") { return arg.status() == status; } MATCHER_P(NameIs, name, "") { return arg->GetSchemaNameInfo().value().name == name; } MATCHER_P2(ColumnIs, name, type, "") { bool match = true; match &= ::testing::ExplainMatchResult(::testing::Eq(name), arg->Name(), result_listener); match &= arg->GetType()->Equals(type); if (!match) { (*result_listener) << "(" << absl::StrJoin( {arg->Name(), arg->GetType()->DebugString()}, ",") << ")"; } return match; } MATCHER_P2(IsKeyColumnOf, table, order, "") { auto pk = table->FindKeyColumn(arg->Name()); if (pk == nullptr) { return ::testing::ExplainMatchResult(::testing::Eq(arg), nullptr, result_listener); } return ::testing::ExplainMatchResult(::testing::Eq(pk->is_descending()), std::string(order) == "DESC", result_listener); } MATCHER_P2(IsInterleavedIn, parent, on_delete, "") { // Check parent-child relationship. EXPECT_EQ(arg->parent(), parent); auto children = parent->children(); auto it = std::find(children.begin(), children.end(), arg); EXPECT_NE(it, children.end()); EXPECT_EQ(arg->on_delete_action(), on_delete); // Check primary keys. auto parent_pk = parent->primary_key(); auto child_pk = arg->primary_key(); EXPECT_LE(parent_pk.size(), child_pk.size()); // Nullability of interleaved null-filtered index key columns can be // different from parent key columns. bool ignore_nullability = arg->owner_index() != nullptr && arg->owner_index()->is_null_filtered(); for (int i = 0; i < parent_pk.size(); ++i) { EXPECT_THAT(child_pk[i]->column(), ColumnIs(parent_pk[i]->column()->Name(), parent_pk[i]->column()->GetType())); EXPECT_EQ(child_pk[i]->is_descending(), parent_pk[i]->is_descending()); EXPECT_EQ(child_pk[i]->column()->declared_max_length(), parent_pk[i]->column()->declared_max_length()); if (!ignore_nullability) { EXPECT_EQ(child_pk[i]->column()->is_nullable(), parent_pk[i]->column()->is_nullable()); } } return true; } MATCHER_P(SourceColumnIs, source, "") { EXPECT_EQ(arg->source_column(), source); EXPECT_EQ(arg->Name(), source->Name()); EXPECT_TRUE(arg->GetType()->Equals(source->GetType())); EXPECT_EQ(arg->declared_max_length(), source->declared_max_length()); return true; } template <typename T> std::string PrintNames(absl::Span<const T* const> nodes) { return absl::StrJoin(nodes, ",", [](std::string* out, const T* node) { absl::StrAppend(out, node->GetSchemaNameInfo().value().name); }); } template <typename T> std::string PrintNames(const std::vector<const T*> nodes) { return PrintNames(absl::Span<const T* const>(nodes)); } #define ASSERT_NOT_NULL(v) AssertNotNull((v), __FILE__, __LINE__) template <typename T> const T* AssertNotNull(const T* value, const char* file, int line) { auto assert_not_null = [](const T* value, const char* file, int line) { // Must be called in a function returning void. ASSERT_NE(value, nullptr) << " at " << file << ":" << line; }; assert_not_null(value, file, line); return value; } class SchemaUpdaterTest : public testing::Test, public testing::WithParamInterface<database_api::DatabaseDialect> { public: SchemaUpdaterTest() : feature_flags_({ .enable_identity_columns = true, .enable_serial_auto_increment = true, .enable_user_defined_functions = true, .enable_search_index = true, .enable_default_time_zone = true, }) {} absl::StatusOr<std::unique_ptr<const Schema>> CreateSchema( absl::Span<const std::string> statements, absl::string_view proto_descriptor_bytes = "", const database_api::DatabaseDialect& dialect = GetParam(), bool use_gsql_to_pg_translation = true); absl::StatusOr<std::unique_ptr<const Schema>> UpdateSchema( const Schema* base_schema, absl::Span<const std::string> statements, absl::string_view proto_descriptor_bytes = "", const database_api::DatabaseDialect& dialect = GetParam(), bool use_gsql_to_pg_translation = true, std::string_view database_id = ""); protected: void SetUp() override { if (GetParam() == database_api::DatabaseDialect::POSTGRESQL) { pg_schema_printer_ = CreateSpangresDirectSchemaPrinter().value(); pg_oid_assigner_ = std::make_unique<PgOidAssigner>(/*enabled=*/true); } else { pg_oid_assigner_ = std::make_unique<PgOidAssigner>(/*enabled=*/false); } } std::unique_ptr<SpangresSchemaPrinter> pg_schema_printer_; zetasql::TypeFactory type_factory_; TableIDGenerator table_id_generator_; ColumnIDGenerator column_id_generator_; std::unique_ptr<PgOidAssigner> pg_oid_assigner_; ScopedEmulatorFeatureFlagsSetter feature_flags_; }; INSTANTIATE_TEST_SUITE_P( SchemaUpdaterPerDialectTests, SchemaUpdaterTest, testing::Values(database_api::DatabaseDialect::GOOGLE_STANDARD_SQL, database_api::DatabaseDialect::POSTGRESQL), [](const testing::TestParamInfo<SchemaUpdaterTest::ParamType>& info) { return database_api::DatabaseDialect_Name(info.param); }); } // namespace test } // namespace backend } // namespace emulator } // namespace spanner } // namespace google #endif // THIRD_PARTY_CLOUD_SPANNER_EMULATOR_BACKEND_SCHEMA_UPDATER_SCHEMA_UPDATER_TESTS_BASE_H_