From 8482442f5d3c989d15169f728878c4f24e166590 Mon Sep 17 00:00:00 2001 From: "A. Googler" Date: Fri, 6 Dec 2024 01:10:37 -0800 Subject: [PATCH] Project import generated by Copybara. GitOrigin-RevId: c13e93f1718ff1e1df6ed5d356a8057aa6337eb8 --- backend/schema/parser/ddl_parser.cc | 45 +- backend/schema/parser/ddl_parser.jjt | 3 +- backend/schema/parser/ddl_parser_test.cc | 102 ++- .../updater/schema_updater_tests/table.cc | 21 + .../cases/check_constraint_read_write.cc | 312 ++++--- .../data/schema_changes/combined.test | 48 ++ .../data/schema_changes/models.test | 32 + .../data/schemas/check_constraint.test | 43 + .../spanner_pg/bootstrap_catalog/BUILD | 5 +- .../bootstrap_catalog_test.cc | 14 + .../spanner_pg_data/pg_proc.dat | 6 + third_party/spanner_pg/catalog/BUILD | 8 +- .../catalog/builtin_spanner_functions.cc | 7 + third_party/spanner_pg/catalog/type.cc | 4 + third_party/spanner_pg/catalog/type.h | 1 - third_party/spanner_pg/catalog/type_test.cc | 35 +- .../ddl/pg_to_spanner_ddl_translator.cc | 89 ++ .../spangres_direct_schema_printer_impl.cc | 53 ++ .../spanner_pg/errors/error_catalog.textproto | 5 + third_party/spanner_pg/interface/BUILD | 14 +- .../postgres_includes/pg-include-list.h | 1 + third_party/spanner_pg/shims/BUILD | 9 - third_party/spanner_pg/shims/catalog_shim.h | 11 - .../spanner_pg/shims/catalog_shim_acl.c | 86 -- .../spanner_pg/shims/catalog_shim_catcache.c | 70 -- .../spanner_pg/shims/catalog_shim_date.c | 133 --- .../spanner_pg/shims/catalog_shim_datetime.c | 806 ------------------ .../shims/catalog_shim_dbcommands.c | 80 -- .../shims/catalog_shim_expected_errors.c | 20 - .../spanner_pg/shims/catalog_shim_fmgr.c | 104 --- .../shims/catalog_shim_pg_constraint.c | 75 -- .../shims/catalog_shim_rewriteHandler.c | 72 -- .../spanner_pg/shims/catalog_shim_test.cc | 4 +- .../spanner_pg/shims/catalog_shim_timestamp.c | 131 --- .../spanner_pg/shims/fake_catalog_shim.c | 59 -- .../src/backend/catalog/pg_constraint.c | 82 +- .../src/backend/commands/dbcommands.c | 43 +- .../src/backend/rewrite/rewriteHandler.c | 157 +--- .../src/backend/storage/lmgr/lock.c | 380 +-------- .../spanner_pg/src/backend/utils/adt/acl.c | 16 +- .../spanner_pg/src/backend/utils/adt/date.c | 19 +- .../src/backend/utils/adt/datetime.c | 91 +- .../src/backend/utils/adt/timestamp.c | 19 +- .../src/backend/utils/cache/catcache.c | 22 +- .../spanner_pg/src/backend/utils/fmgr/fmgr.c | 113 +-- .../spanner_pg/src/backend/utils/misc/guc.c | 56 +- third_party/spanner_pg/transformer/BUILD | 1 + .../spanner_pg/transformer/expr_test.cc | 1 + .../spanner_pg/transformer/forward_query.cc | 12 +- .../spanner_pg/transformer/query_test.cc | 20 +- third_party/spanner_pg/util/BUILD | 25 + .../uuid_conversion.cc} | 59 +- third_party/spanner_pg/util/uuid_conversion.h | 48 ++ .../spanner_pg/util/uuid_conversion_test.cc | 61 ++ 54 files changed, 1002 insertions(+), 2731 deletions(-) create mode 100644 tests/conformance/data/schemas/check_constraint.test delete mode 100644 third_party/spanner_pg/shims/catalog_shim_acl.c delete mode 100644 third_party/spanner_pg/shims/catalog_shim_catcache.c delete mode 100644 third_party/spanner_pg/shims/catalog_shim_date.c delete mode 100644 third_party/spanner_pg/shims/catalog_shim_datetime.c delete mode 100644 third_party/spanner_pg/shims/catalog_shim_dbcommands.c delete mode 100644 third_party/spanner_pg/shims/catalog_shim_fmgr.c delete mode 100644 third_party/spanner_pg/shims/catalog_shim_pg_constraint.c delete mode 100644 third_party/spanner_pg/shims/catalog_shim_rewriteHandler.c delete mode 100644 third_party/spanner_pg/shims/catalog_shim_timestamp.c rename third_party/spanner_pg/{interface/stub_bootstrap_catalog_accessor.cc => util/uuid_conversion.cc} (54%) create mode 100644 third_party/spanner_pg/util/uuid_conversion.h create mode 100644 third_party/spanner_pg/util/uuid_conversion_test.cc diff --git a/backend/schema/parser/ddl_parser.cc b/backend/schema/parser/ddl_parser.cc index e8b0701f..976cedef 100644 --- a/backend/schema/parser/ddl_parser.cc +++ b/backend/schema/parser/ddl_parser.cc @@ -815,8 +815,14 @@ void VisitColumnDefaultClauseNode(const SimpleNode* node, absl::StrCat(ExtractTextForNode(child, ddl_text))); } -void VisitColumnNode(const SimpleNode* node, ColumnDefinition* column, - absl::string_view ddl_text, +// If containing_table is not null, then we support parsing a PRIMARY KEY clause +// attached to this column; if present, this column's name will be set as the +// sole primary key component in the containing table, or a syntax error will be +// generated if the table's primary key has already been set by a previous +// column. It is expected that higher-level parsing code using this feature will +// also check that there is no table-level PRIMARY KEY clause in that case. +void VisitColumnNode(const SimpleNode* node, absl::string_view ddl_text, + ColumnDefinition* column, CreateTable* containing_table, std::vector* errors) { CheckNode(node, JJTCOLUMN_DEF); column->set_column_name(GetChildNode(node, 0, JJTNAME)->image()); @@ -833,6 +839,18 @@ void VisitColumnNode(const SimpleNode* node, ColumnDefinition* column, case JJTNOT_NULL: column->set_not_null(true); break; + case JJTPRIMARY_KEY: + if (containing_table != nullptr) { + if (containing_table->primary_key().empty()) { + containing_table->add_primary_key()->set_key_name( + column->column_name()); + } else { + errors->push_back("Multiple columns declared as PRIMARY KEY"); + } + } else { + errors->push_back("Unexpected PRIMARY KEY clause"); + } + break; case JJTGENERATION_CLAUSE: VisitGenerationClauseNode(child, column, ddl_text); break; @@ -1138,11 +1156,13 @@ void VisitCreateTableNode(const SimpleNode* node, CreateTable* table, GetQualifiedIdentifier(GetChildNode(node, offset, JJTNAME))); offset++; + bool has_primary_key = false; + for (int i = offset; i < node->jjtGetNumChildren(); i++) { SimpleNode* child = GetChildNode(node, i); switch (child->getId()) { case JJTCOLUMN_DEF: - VisitColumnNode(child, table->add_column(), ddl_text, errors); + VisitColumnNode(child, ddl_text, table->add_column(), table, errors); break; case JJTFOREIGN_KEY: VisitForeignKeyNode(child, table->add_foreign_key(), errors); @@ -1152,7 +1172,12 @@ void VisitCreateTableNode(const SimpleNode* node, CreateTable* table, errors); break; case JJTPRIMARY_KEY: - VisitKeyNode(child, table->mutable_primary_key(), errors); + if (table->primary_key().empty()) { + VisitKeyNode(child, table->mutable_primary_key(), errors); + } else { + errors->push_back("Cannot specify both table and column PRIMARY KEY"); + } + has_primary_key = true; break; case JJTSYNONYM_CLAUSE: table->set_synonym(GetChildNode(child, 0)->image()); @@ -1168,6 +1193,11 @@ void VisitCreateTableNode(const SimpleNode* node, CreateTable* table, ABSL_LOG(FATAL) << "Unexpected table info: " << child->toString(); } } + + if (table->primary_key().empty() && !has_primary_key) { + // Even for singleton tables, a table-level PRIMARY KEY() must be specified. + errors->push_back("Must specify either table or column PRIMARY KEY"); + } } void VisitCreateFunctionNode(const SimpleNode* node, @@ -2140,9 +2170,9 @@ void VisitAlterTableNode(const SimpleNode* node, absl::string_view ddl_text, alter_table->mutable_add_column()->set_existence_modifier( IF_NOT_EXISTS); } - VisitColumnNode(GetChildNode(node, offset, JJTCOLUMN_DEF), + VisitColumnNode(GetChildNode(node, offset, JJTCOLUMN_DEF), ddl_text, alter_table->mutable_add_column()->mutable_column(), - ddl_text, errors); + /*containing_table=*/nullptr, errors); } break; case JJTDROP_COLUMN: { SimpleNode* column = GetChildNode(node, 2, JJTCOLUMN_NAME); @@ -2417,7 +2447,8 @@ void VisitModelColumnList(const SimpleNode* node, for (int i = 0; i < node->jjtGetNumChildren(); i++) { SimpleNode* child = GetChildNode(node, i); CheckNode(child, JJTCOLUMN_DEF); - VisitColumnNode(child, columns->Add(), ddl_text, errors); + VisitColumnNode(child, ddl_text, columns->Add(), + /*containing_table=*/nullptr, errors); } } diff --git a/backend/schema/parser/ddl_parser.jjt b/backend/schema/parser/ddl_parser.jjt index f9ec75de..1f09fdf1 100644 --- a/backend/schema/parser/ddl_parser.jjt +++ b/backend/schema/parser/ddl_parser.jjt @@ -256,7 +256,7 @@ void create_table_statement() : [ if_not_exists() ] qualified_identifier() #name "(" [ table_element() ( LOOKAHEAD(2) "," table_element() )* [ "," ] ] ")" - primary_key() + [ primary_key() ] [ LOOKAHEAD(2) "," table_interleave_clause() ] [ LOOKAHEAD(2) "," row_deletion_policy_clause() ] } @@ -281,6 +281,7 @@ void column_def() : | identity_column_clause() ] [ #hidden ] + [ #primary_key ] [ options_clause() ] } diff --git a/backend/schema/parser/ddl_parser_test.cc b/backend/schema/parser/ddl_parser_test.cc index e86d3015..a69ae6ad 100644 --- a/backend/schema/parser/ddl_parser_test.cc +++ b/backend/schema/parser/ddl_parser_test.cc @@ -272,15 +272,81 @@ TEST(ParseCreateTable, CannotParseCreateTableWithoutName) { } TEST(ParseCreateTable, CannotParseCreateTableWithoutPrimaryKey) { - EXPECT_THAT(ParseDDLStatement( - R"sql( + EXPECT_THAT( + ParseDDLStatement( + R"sql( CREATE TABLE Users ( UserId INT64 NOT NULL, Name STRING(MAX) ) )sql"), + StatusIs(StatusCode::kInvalidArgument, + HasSubstr("Must specify either table or column PRIMARY KEY"))); +} + +TEST(ParseCreateTable, CanParseCreateTableWithPrimaryKeyOnAColumn) { + EXPECT_THAT( + ParseDDLStatement( + R"sql( + CREATE TABLE Users ( + UserId INT64 NOT NULL PRIMARY KEY, + Name STRING(MAX) + ) + )sql"), + IsOkAndHolds(test::EqualsProto( + R"pb( + create_table { + table_name: "Users" + column { column_name: "UserId" type: INT64 not_null: true } + column { column_name: "Name" type: STRING } + primary_key { key_name: "UserId" } + } + )pb"))); +} + +TEST(ParseCreateTable, CannotParseCreateTablePrimaryKeyOnTwoColumns) { + EXPECT_THAT(ParseDDLStatement( + R"sql( + CREATE TABLE Users ( + UserId INT64 NOT NULL PRIMARY KEY, + Name STRING(MAX) PRIMARY KEY + ) + )sql"), StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Expecting 'PRIMARY' but found 'EOF'"))); + HasSubstr("Multiple columns declared as PRIMARY KEY"))); +} + +TEST(ParseCreateTable, CannotParseCreateTablePrimaryKeyInColumnAndInTable) { + EXPECT_THAT( + ParseDDLStatement( + R"sql( + CREATE TABLE Users ( + UserId INT64 NOT NULL PRIMARY KEY, + Name STRING(MAX) + ) PRIMARY KEY (UserId) + )sql"), + StatusIs(StatusCode::kInvalidArgument, + HasSubstr("Cannot specify both table and column PRIMARY KEY"))); +} + +TEST(ParseCreateTable, CannotParseAlterTableWithPrimaryKeyInAddColumn) { + EXPECT_THAT(ParseDDLStatement( + R"sql( + ALTER TABLE Users + ADD COLUMN UserId INT64 NOT NULL PRIMARY KEY + )sql"), + StatusIs(StatusCode::kInvalidArgument, + HasSubstr("Unexpected PRIMARY KEY clause"))); +} + +TEST(ParseCreateTable, CannotParseAlterTableWithPrimaryKeyInAlterColumn) { + EXPECT_THAT(ParseDDLStatement( + R"sql( + ALTER TABLE Users + ALTER COLUMN UserId INT64 NOT NULL PRIMARY KEY + )sql"), + StatusIs(StatusCode::kInvalidArgument, + HasSubstr("Expecting 'EOF' but found 'PRIMARY'"))); } TEST(ParseCreateTable, CanParseCreateTableWithOnlyAKeyColumn) { @@ -5176,6 +5242,36 @@ TEST(ParseCreateModel, ParseCreateModel) { )sql"), StatusIs(StatusCode::kInvalidArgument, HasSubstr("Encountered ',' while parsing: column_type"))); + + // Model has an unexpected PRIMARY KEY in input column + EXPECT_THAT(ParseDDLStatement(R"sql( + CREATE MODEL m + INPUT ( + f1 INT64, + f2 STRING(MAX) PRIMARY KEY + ) + OUTPUT ( + l1 BOOL, + l2 ARRAY + ) + )sql"), + StatusIs(StatusCode::kInvalidArgument, + HasSubstr("Unexpected PRIMARY KEY clause"))); + + // Model has an unexpected PRIMARY KEY in output column + EXPECT_THAT(ParseDDLStatement(R"sql( + CREATE MODEL m + INPUT ( + f1 INT64, + f2 STRING(MAX) + ) + OUTPUT ( + l1 BOOL PRIMARY KEY, + l2 ARRAY + ) + )sql"), + StatusIs(StatusCode::kInvalidArgument, + HasSubstr("Unexpected PRIMARY KEY clause"))); } TEST(ParseAlterModel, ParseAlterModel) { diff --git a/backend/schema/updater/schema_updater_tests/table.cc b/backend/schema/updater/schema_updater_tests/table.cc index 84266074..fe3047f6 100644 --- a/backend/schema/updater/schema_updater_tests/table.cc +++ b/backend/schema/updater/schema_updater_tests/table.cc @@ -67,6 +67,27 @@ TEST_P(SchemaUpdaterTest, CreateTable_SingleKey) { EXPECT_THAT(col2, testing::Not(IsKeyColumnOf(t, "ASC"))); } +TEST_P(SchemaUpdaterTest, CreateTable_SingleKeyInline) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto schema, CreateSchema({R"( + CREATE TABLE T( + col1 INT64 PRIMARY KEY, + col2 STRING(MAX) + ))"})); + + auto t = schema->FindTable("T"); + EXPECT_NE(t, nullptr); + EXPECT_EQ(t->columns().size(), 2); + EXPECT_EQ(t->primary_key().size(), 1); + + auto col1 = t->columns()[0]; + EXPECT_THAT(col1, ColumnIs("col1", types::Int64Type())); + EXPECT_THAT(col1, IsKeyColumnOf(t, "ASC")); + + auto col2 = t->columns()[1]; + EXPECT_THAT(col2, ColumnIs("col2", types::StringType())); + EXPECT_THAT(col2, testing::Not(IsKeyColumnOf(t, "ASC"))); +} + TEST_P(SchemaUpdaterTest, CreateTable_MultiKey) { std::unique_ptr schema; // Changing the ordering for key columns is unsupported in PG. diff --git a/tests/conformance/cases/check_constraint_read_write.cc b/tests/conformance/cases/check_constraint_read_write.cc index 2e14aa9c..796d3b3e 100644 --- a/tests/conformance/cases/check_constraint_read_write.cc +++ b/tests/conformance/cases/check_constraint_read_write.cc @@ -14,9 +14,12 @@ // limitations under the License. // -#include #include +#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 "tests/conformance/common/database_test_base.h" @@ -28,228 +31,273 @@ namespace { using zetasql_base::testing::StatusIs; -class CheckConstraintTest : public DatabaseTest { +class CheckConstraintTest + : public DatabaseTest, + public testing::WithParamInterface { protected: absl::Status SetUpDatabase() override { - return SetSchema({ - "CREATE TABLE T(" - " K INT64," - " V INT64," - " V_STR STRING(MAX)," - " CONSTRAINT v_gt_zero CHECK (V > 0)," - " CONSTRAINT v_left_shift_gt_zero CHECK ((V << 1) > 0)," - " CONSTRAINT v_str_gt_zero CHECK(V_STR > '0')," - " ) PRIMARY KEY (K)", - "CREATE TABLE T_GC(" - " K INT64," - " V INT64," - " G1 INT64 AS (V) STORED," - " G2 INT64 AS (G1) STORED," - " G3 INT64 AS (V + 5) STORED," - " CONSTRAINT g1_gt_zero CHECK (G1 > 0)," - " CONSTRAINT g2_gt_zero CHECK(G2 > 0)," - " CONSTRAINT g3_gt_ten CHECK(G3 > 10)," - " ) PRIMARY KEY (K)", - }); + return SetSchemaFromFile("check_constraint.test"); + } + + void SetUp() override { + dialect_ = GetParam(); + DatabaseTest::SetUp(); } }; -TEST_F(CheckConstraintTest, Insert) { - ZETASQL_ASSERT_OK(Insert("T", {"K", "V"}, {1, 10})); - EXPECT_THAT(Read("T", {"K", "V"}, Key(1)), IsOkAndHoldsRow({1, 10})); +INSTANTIATE_TEST_SUITE_P( + PerDialectCheckConstraintTests, CheckConstraintTest, + testing::Values(database_api::DatabaseDialect::GOOGLE_STANDARD_SQL, + database_api::DatabaseDialect::POSTGRESQL), + [](const testing::TestParamInfo& info) { + return database_api::DatabaseDialect_Name(info.param); + }); - ZETASQL_ASSERT_OK(Insert("T", {"K", "V_STR"}, {2, "10"})); - EXPECT_THAT(Read("T", {"K", "V_STR"}, Key(2)), IsOkAndHoldsRow({2, "10"})); +TEST_P(CheckConstraintTest, Insert) { + ZETASQL_ASSERT_OK(Insert("t", {"k", "v"}, {1, 10})); + EXPECT_THAT(Read("t", {"k", "v"}, Key(1)), IsOkAndHoldsRow({1, 10})); - ZETASQL_ASSERT_OK(Insert("T_GC", {"K", "V"}, {1, 10})); - EXPECT_THAT(Read("T_GC", {"K", "V", "G1", "G2", "G3"}, Key(1)), - IsOkAndHoldsRow({1, 10, 10, 10, 15})); + ZETASQL_ASSERT_OK(Insert("t", {"k", "v_str"}, {2, "10"})); + EXPECT_THAT(Read("t", {"k", "v_str"}, Key(2)), IsOkAndHoldsRow({2, "10"})); + + ZETASQL_ASSERT_OK(Insert("t_gc", {"k", "v"}, {1, 10})); + + if (GetParam() == database_api::DatabaseDialect::POSTGRESQL) { + // g2 is not defined in PG as generated columns cannot reference other + // generated columns. + EXPECT_THAT(Read("t_gc", {"k", "v", "g1", "g3"}, Key(1)), + IsOkAndHoldsRow({1, 10, 10, 15})); + } else { + EXPECT_THAT(Read("t_gc", {"k", "v", "g1", "g2", "g3"}, Key(1)), + IsOkAndHoldsRow({1, 10, 10, 10, 15})); + } } -TEST_F(CheckConstraintTest, InsertNull) { - ZETASQL_ASSERT_OK(Insert("T", {"K", "V"}, {1, Null()})); - EXPECT_THAT(Read("T", {"K", "V"}, Key(1)), +TEST_P(CheckConstraintTest, InsertNull) { + ZETASQL_ASSERT_OK(Insert("t", {"k", "v"}, {1, Null()})); + EXPECT_THAT(Read("t", {"k", "v"}, Key(1)), IsOkAndHoldsRow({1, Null()})); - ZETASQL_ASSERT_OK(Insert("T_GC", {"K", "V"}, {1, Null()})); - EXPECT_THAT(Read("T_GC", {"K", "V", "G1", "G2", "G3"}, Key(1)), - IsOkAndHoldsRow({1, Null(), Null(), - Null(), Null()})); + ZETASQL_ASSERT_OK(Insert("t_gc", {"k", "v"}, {1, Null()})); + + if (GetParam() == database_api::DatabaseDialect::POSTGRESQL) { + // g2 is not defined in PG as generated columns cannot reference other + // generated columns. + EXPECT_THAT(Read("t_gc", {"k", "v", "g1", "g3"}, Key(1)), + IsOkAndHoldsRow({1, Null(), Null(), + Null()})); + } else { + EXPECT_THAT(Read("t_gc", {"k", "v", "g1", "g2", "g3"}, Key(1)), + IsOkAndHoldsRow({1, Null(), Null(), + Null(), Null()})); + } } -TEST_F(CheckConstraintTest, InsertWithCoercion) { - // v_left_shift_gt_zero ZETASQL_VLOG ((V << 1) > 0) is OK. - ZETASQL_ASSERT_OK(Insert("T", {"K", "V"}, {1, (int32_t)1073741824})); - EXPECT_THAT(Read("T", {"K", "V"}, Key(1)), +TEST_P(CheckConstraintTest, InsertWithCoercion) { + // v_left_shift_gt_zero ZETASQL_VLOG ((v << 1) > 0) is OK. + ZETASQL_ASSERT_OK(Insert("t", {"k", "v"}, {1, (int32_t)1073741824})); + EXPECT_THAT(Read("t", {"k", "v"}, Key(1)), IsOkAndHoldsRow({1, (int64_t)1073741824})); } -TEST_F(CheckConstraintTest, InsertViolation) { - EXPECT_THAT(Insert("T", {"K", "V"}, {1, -1}), +TEST_P(CheckConstraintTest, InsertViolation) { + EXPECT_THAT(Insert("t", {"k", "v"}, {1, -1}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T`.`v_gt_zero` is violated"))); + "Check constraint `t`.`v_gt_zero` is violated"))); EXPECT_THAT( - Insert("T", {"K", "V_STR"}, {1, "-1"}), + Insert("t", {"k", "v_str"}, {1, "-1"}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T`.`v_str_gt_zero` is violated"))); + "Check constraint `t`.`v_str_gt_zero` is violated"))); - EXPECT_THAT(Insert("T_GC", {"K", "V"}, {1, 1}), + EXPECT_THAT(Insert("t_gc", {"k", "v"}, {1, 1}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T_GC`.`g3_gt_ten` is violated"))); + "Check constraint `t_gc`.`g3_gt_ten` is violated"))); } -TEST_F(CheckConstraintTest, Update) { - ZETASQL_ASSERT_OK(Insert("T", {"K", "V"}, {1, 10})); - ZETASQL_ASSERT_OK(Update("T", {"K", "V"}, {1, 5})); - EXPECT_THAT(Read("T", {"K", "V"}, Key(1)), IsOkAndHoldsRow({1, 5})); - - ZETASQL_ASSERT_OK(Insert("T", {"K", "V_STR"}, {2, "10"})); - ZETASQL_ASSERT_OK(Update("T", {"K", "V_STR"}, {2, "5"})); - EXPECT_THAT(Read("T", {"K", "V_STR"}, Key(2)), IsOkAndHoldsRow({2, "5"})); - - ZETASQL_ASSERT_OK(Insert("T_GC", {"K", "V"}, {1, 10})); - ZETASQL_ASSERT_OK(Update("T_GC", {"K", "V"}, {1, 15})); - EXPECT_THAT(Read("T_GC", {"K", "V", "G1", "G2", "G3"}, Key(1)), - IsOkAndHoldsRow({1, 15, 15, 15, 20})); +TEST_P(CheckConstraintTest, Update) { + ZETASQL_ASSERT_OK(Insert("t", {"k", "v"}, {1, 10})); + ZETASQL_ASSERT_OK(Update("t", {"k", "v"}, {1, 5})); + EXPECT_THAT(Read("t", {"k", "v"}, Key(1)), IsOkAndHoldsRow({1, 5})); + + ZETASQL_ASSERT_OK(Insert("t", {"k", "v_str"}, {2, "10"})); + ZETASQL_ASSERT_OK(Update("t", {"k", "v_str"}, {2, "5"})); + EXPECT_THAT(Read("t", {"k", "v_str"}, Key(2)), IsOkAndHoldsRow({2, "5"})); + + ZETASQL_ASSERT_OK(Insert("t_gc", {"k", "v"}, {1, 10})); + ZETASQL_ASSERT_OK(Update("t_gc", {"k", "v"}, {1, 15})); + + if (GetParam() == database_api::DatabaseDialect::POSTGRESQL) { + // g2 is not defined in PG as generated columns cannot reference other + // generated columns. + EXPECT_THAT(Read("t_gc", {"k", "v", "g1", "g3"}, Key(1)), + IsOkAndHoldsRow({1, 15, 15, 20})); + } else { + EXPECT_THAT(Read("t_gc", {"k", "v", "g1", "g2", "g3"}, Key(1)), + IsOkAndHoldsRow({1, 15, 15, 15, 20})); + } } -TEST_F(CheckConstraintTest, UpdateNull) { - ZETASQL_ASSERT_OK(Insert("T", {"K", "V"}, {1, 10})); - ZETASQL_ASSERT_OK(Update("T", {"K", "V"}, {1, Null()})); - EXPECT_THAT(Read("T", {"K", "V"}, Key(1)), +TEST_P(CheckConstraintTest, UpdateNull) { + ZETASQL_ASSERT_OK(Insert("t", {"k", "v"}, {1, 10})); + ZETASQL_ASSERT_OK(Update("t", {"k", "v"}, {1, Null()})); + EXPECT_THAT(Read("t", {"k", "v"}, Key(1)), IsOkAndHoldsRow({1, Null()})); - ZETASQL_ASSERT_OK(Insert("T_GC", {"K", "V"}, {1, 10})); - ZETASQL_ASSERT_OK(Update("T_GC", {"K", "V"}, {1, Null()})); - EXPECT_THAT(Read("T_GC", {"K", "V", "G1", "G2", "G3"}, Key(1)), - IsOkAndHoldsRow({1, Null(), Null(), - Null(), Null()})); + ZETASQL_ASSERT_OK(Insert("t_gc", {"k", "v"}, {1, 10})); + ZETASQL_ASSERT_OK(Update("t_gc", {"k", "v"}, {1, Null()})); + + if (GetParam() == database_api::DatabaseDialect::POSTGRESQL) { + // g2 is not defined in PG as generated columns cannot reference other + // generated columns. + EXPECT_THAT(Read("t_gc", {"k", "v", "g1", "g3"}, Key(1)), + IsOkAndHoldsRow({1, Null(), Null(), + Null()})); + } else { + EXPECT_THAT(Read("t_gc", {"k", "v", "g1", "g2", "g3"}, Key(1)), + IsOkAndHoldsRow({1, Null(), Null(), + Null(), Null()})); + } - ZETASQL_ASSERT_OK(Insert("T", {"K", "V_STR"}, {2, "10"})); - ZETASQL_ASSERT_OK(Update("T", {"K", "V_STR"}, {2, Null()})); - EXPECT_THAT(Read("T", {"K", "V_STR"}, Key(2)), + ZETASQL_ASSERT_OK(Insert("t", {"k", "v_str"}, {2, "10"})); + ZETASQL_ASSERT_OK(Update("t", {"k", "v_str"}, {2, Null()})); + EXPECT_THAT(Read("t", {"k", "v_str"}, Key(2)), IsOkAndHoldsRow({2, Null()})); } -TEST_F(CheckConstraintTest, UpdateViolation) { - ZETASQL_ASSERT_OK(Insert("T", {"K", "V"}, {1, 10})); - EXPECT_THAT(Update("T", {"K", "V"}, {1, -1}), +TEST_P(CheckConstraintTest, UpdateViolation) { + ZETASQL_ASSERT_OK(Insert("t", {"k", "v"}, {1, 10})); + EXPECT_THAT(Update("t", {"k", "v"}, {1, -1}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T`.`v_gt_zero` is violated"))); + "Check constraint `t`.`v_gt_zero` is violated"))); - ZETASQL_ASSERT_OK(Insert("T_GC", {"K", "V"}, {1, 10})); - EXPECT_THAT(Update("T_GC", {"K", "V"}, {1, 1}), + ZETASQL_ASSERT_OK(Insert("t_gc", {"k", "v"}, {1, 10})); + EXPECT_THAT(Update("t_gc", {"k", "v"}, {1, 1}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T_GC`.`g3_gt_ten` is violated"))); + "Check constraint `t_gc`.`g3_gt_ten` is violated"))); - ZETASQL_ASSERT_OK(Insert("T", {"K", "V_STR"}, {2, "10"})); + ZETASQL_ASSERT_OK(Insert("t", {"k", "v_str"}, {2, "10"})); EXPECT_THAT( - Update("T", {"K", "V_STR"}, {2, "-1"}), + Update("t", {"k", "v_str"}, {2, "-1"}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T`.`v_str_gt_zero` is violated"))); + "Check constraint `t`.`v_str_gt_zero` is violated"))); } -TEST_F(CheckConstraintTest, UpdateNonExistingKey) { - EXPECT_THAT(Update("T", {"K", "V"}, {1, 10}), +TEST_P(CheckConstraintTest, UpdateNonExistingKey) { + EXPECT_THAT(Update("t", {"k", "v"}, {1, 10}), StatusIs(absl::StatusCode::kNotFound)); } -TEST_F(CheckConstraintTest, InsertOrUpdate) { - ZETASQL_ASSERT_OK(InsertOrUpdate("T", {"K", "V", "V_STR"}, {1, 10, "10"})); - EXPECT_THAT(Read("T", {"K", "V", "V_STR"}, Key(1)), +TEST_P(CheckConstraintTest, InsertOrUpdate) { + ZETASQL_ASSERT_OK(InsertOrUpdate("t", {"k", "v", "v_str"}, {1, 10, "10"})); + EXPECT_THAT(Read("t", {"k", "v", "v_str"}, Key(1)), IsOkAndHoldsRow({1, 10, "10"})); - ZETASQL_ASSERT_OK(InsertOrUpdate("T", {"K", "V", "V_STR"}, {1, 5, "5"})); - EXPECT_THAT(Read("T", {"K", "V", "V_STR"}, Key(1)), + ZETASQL_ASSERT_OK(InsertOrUpdate("t", {"k", "v", "v_str"}, {1, 5, "5"})); + EXPECT_THAT(Read("t", {"k", "v", "v_str"}, Key(1)), IsOkAndHoldsRow({1, 5, "5"})); } -TEST_F(CheckConstraintTest, InsertOrUpdateViolation) { - EXPECT_THAT(InsertOrUpdate("T", {"K", "V", "V_STR"}, {1, -1, "10"}), +TEST_P(CheckConstraintTest, InsertOrUpdateViolation) { + EXPECT_THAT(InsertOrUpdate("t", {"k", "v", "v_str"}, {1, -1, "10"}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T`.`v_gt_zero` is violated"))); + "Check constraint `t`.`v_gt_zero` is violated"))); EXPECT_THAT( - InsertOrUpdate("T", {"K", "V", "V_STR"}, {1, 10, "-1"}), + InsertOrUpdate("t", {"k", "v", "v_str"}, {1, 10, "-1"}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T`.`v_str_gt_zero` is violated"))); + "Check constraint `t`.`v_str_gt_zero` is violated"))); } -TEST_F(CheckConstraintTest, Replace) { - ZETASQL_ASSERT_OK(Insert("T", {"K", "V", "V_STR"}, {1, 10, "10"})); - ZETASQL_ASSERT_OK(Replace("T", {"K", "V", "V_STR"}, {1, 5, "5"})); - EXPECT_THAT(Read("T", {"K", "V", "V_STR"}, Key(1)), +TEST_P(CheckConstraintTest, Replace) { + ZETASQL_ASSERT_OK(Insert("t", {"k", "v", "v_str"}, {1, 10, "10"})); + ZETASQL_ASSERT_OK(Replace("t", {"k", "v", "v_str"}, {1, 5, "5"})); + EXPECT_THAT(Read("t", {"k", "v", "v_str"}, Key(1)), IsOkAndHoldsRow({1, 5, "5"})); } -TEST_F(CheckConstraintTest, ReplaceViolation) { - ZETASQL_ASSERT_OK(Insert("T", {"K", "V", "V_STR"}, {1, 10, "10"})); - EXPECT_THAT(Replace("T", {"K", "V", "V_STR"}, {1, -1, "10"}), +TEST_P(CheckConstraintTest, ReplaceViolation) { + ZETASQL_ASSERT_OK(Insert("t", {"k", "v", "v_str"}, {1, 10, "10"})); + EXPECT_THAT(Replace("t", {"k", "v", "v_str"}, {1, -1, "10"}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T`.`v_gt_zero` is violated"))); + "Check constraint `t`.`v_gt_zero` is violated"))); EXPECT_THAT( - Replace("T", {"K", "V", "V_STR"}, {1, 10, "-1"}), + Replace("t", {"k", "v", "v_str"}, {1, 10, "-1"}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T`.`v_str_gt_zero` is violated"))); + "Check constraint `t`.`v_str_gt_zero` is violated"))); } -TEST_F(CheckConstraintTest, DML) { +TEST_P(CheckConstraintTest, DML) { ZETASQL_ASSERT_OK( - CommitDml({SqlStatement("INSERT T(K, V, V_STR) VALUES (1, 1, '1')"), - SqlStatement("UPDATE T SET V = 2, V_STR = '2' WHERE K = 1")})); - EXPECT_THAT(Query("SELECT K, V, V_STR FROM T"), + CommitDml({SqlStatement("INSERT INTO t(k, v, v_str) VALUES (1, 1, '1')"), + SqlStatement("UPDATE t SET v = 2, v_str = '2' WHERE k = 1")})); + EXPECT_THAT(Query("SELECT k, v, v_str FROM t"), IsOkAndHoldsRows({{1, 2, "2"}})); - ZETASQL_ASSERT_OK(CommitDml({SqlStatement("INSERT T_GC(K, V) VALUES (1, 10)"), - SqlStatement("UPDATE T_GC SET V = 20 WHERE K = 1")})); - EXPECT_THAT(Query("SELECT K, V, G1, G2, G3 FROM T_GC"), - IsOkAndHoldsRows({{1, 20, 20, 20, 25}})); + ZETASQL_ASSERT_OK(CommitDml({SqlStatement("INSERT INTO t_gc(k, v) VALUES (1, 10)"), + SqlStatement("UPDATE t_gc SET v = 20 WHERE k = 1")})); + + if (GetParam() == database_api::DatabaseDialect::POSTGRESQL) { + // g2 is not defined in PG as generated columns cannot reference other + // generated columns. + EXPECT_THAT(Query("SELECT k, v, g1, g3 FROM t_gc"), + IsOkAndHoldsRows({{1, 20, 20, 25}})); + } else { + EXPECT_THAT(Query("SELECT k, v, g1, g2, g3 FROM t_gc"), + IsOkAndHoldsRows({{1, 20, 20, 20, 25}})); + } } -TEST_F(CheckConstraintTest, DMLViolation) { - EXPECT_THAT(CommitDml({SqlStatement("INSERT T(K, V) VALUES (1, -1)")}), +TEST_P(CheckConstraintTest, DMLViolation) { + EXPECT_THAT(CommitDml({SqlStatement("INSERT INTO t(k, v) VALUES (1, -1)")}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T`.`v_gt_zero` is violated"))); - EXPECT_THAT(CommitDml({SqlStatement("INSERT T_GC(K, V) VALUES (1, 1)")}), + "Check constraint `t`.`v_gt_zero` is violated"))); + EXPECT_THAT(CommitDml({SqlStatement("INSERT INTO t_gc(k, v) VALUES (1, 1)")}), StatusIs(absl::StatusCode::kOutOfRange, testing::HasSubstr( - "Check constraint `T_GC`.`g3_gt_ten` is violated"))); + "Check constraint `t_gc`.`g3_gt_ten` is violated"))); } -TEST_F(CheckConstraintTest, MultipleMutationPerRow) { +TEST_P(CheckConstraintTest, MultipleMutationPerRow) { ZETASQL_ASSERT_OK(Commit({ - MakeInsert("T", {"K", "V", "V_STR"}, 1, -1, "1"), - MakeUpdate("T", {"K", "V", "V_STR"}, 1, 1, "-2"), - MakeUpdate("T", {"K", "V", "V_STR"}, 1, 2, "2"), - MakeInsert("T", {"K", "V", "V_STR"}, 2, 1, "1"), - MakeDelete("T", Singleton(2)), + MakeInsert("t", {"k", "v", "v_str"}, 1, -1, "1"), + MakeUpdate("t", {"k", "v", "v_str"}, 1, 1, "-2"), + MakeUpdate("t", {"k", "v", "v_str"}, 1, 2, "2"), + MakeInsert("t", {"k", "v", "v_str"}, 2, 1, "1"), + MakeDelete("t", Singleton(2)), })); - EXPECT_THAT(ReadAll("T", + EXPECT_THAT(ReadAll("t", { - "K", - "V", - "V_STR", + "k", + "v", + "v_str", }), IsOkAndHoldsRows({{1, 2, "2"}})); ZETASQL_ASSERT_OK(Commit({ - MakeInsert("T_GC", {"K", "V"}, 1, 0), - MakeUpdate("T_GC", {"K", "V"}, 1, 10), + MakeInsert("t_gc", {"k", "v"}, 1, 0), + MakeUpdate("t_gc", {"k", "v"}, 1, 10), })); - EXPECT_THAT(ReadAll("T_GC", {"K", "V", "G1", "G2", "G3"}), - IsOkAndHoldsRows({{1, 10, 10, 10, 15}})); + if (GetParam() == database_api::DatabaseDialect::POSTGRESQL) { + // g2 is not defined in PG as generated columns cannot reference other + // generated columns. + EXPECT_THAT(ReadAll("t_gc", {"k", "v", "g1", "g3"}), + IsOkAndHoldsRows({{1, 10, 10, 15}})); + } else { + EXPECT_THAT(ReadAll("t_gc", {"k", "v", "g1", "g2", "g3"}), + IsOkAndHoldsRows({{1, 10, 10, 10, 15}})); + } } } // namespace diff --git a/tests/conformance/data/schema_changes/combined.test b/tests/conformance/data/schema_changes/combined.test index f87e6ffa..60d0f875 100644 --- a/tests/conformance/data/schema_changes/combined.test +++ b/tests/conformance/data/schema_changes/combined.test @@ -69,6 +69,54 @@ CREATE TABLE Users ( -- ERROR:.* Duplicate column name Users.UserName. == +# PRIMARY KEY on a column +CREATE TABLE Users ( + UserId INT64 NOT NULL PRIMARY KEY, + Name STRING(MAX) +); +-- +CREATE TABLE Users ( + UserId INT64 NOT NULL, + Name STRING(MAX), +) PRIMARY KEY(UserId); +== +# PRIMARY KEY on two columns is a parse error. +# --regex +CREATE TABLE Users ( + UserId INT64 NOT NULL PRIMARY KEY, + Name STRING(MAX) PRIMARY KEY +); +-- +ERROR:.* Multiple columns declared as PRIMARY KEY +== +# PRIMARY KEY on ALTER TABLE ADD COLUMN is a parse error +# --regex +CREATE TABLE Users ( + Name STRING(MAX) PRIMARY KEY +); +ALTER TABLE Users ADD COLUMN UserId INT64 NOT NULL PRIMARY KEY; +-- +ERROR:.* Unexpected PRIMARY KEY clause +== +# PRIMARY KEY on ALTER TABLE ALTER COLUMN is a parse error +# --regex +CREATE TABLE Users ( + UserId INT64 NOT NULL, + Name STRING(MAX) PRIMARY KEY +); +ALTER TABLE Users ALTER COLUMN UserId INT64 NOT NULL PRIMARY KEY; +-- +ERROR:.* Expecting 'EOF' but found 'PRIMARY' +== +# PRIMARY KEY on a column and the table is a parse error. +# --regex +CREATE TABLE Users ( + UserId INT64 NOT NULL PRIMARY KEY, + Name STRING(MAX) +) PRIMARY KEY(UserId); +-- +ERROR:.* Cannot specify both table and column PRIMARY KEY +== # Primary key references a nonexistent column. # --regex CREATE TABLE Users ( diff --git a/tests/conformance/data/schema_changes/models.test b/tests/conformance/data/schema_changes/models.test index 5a3a9028..a672de14 100644 --- a/tests/conformance/data/schema_changes/models.test +++ b/tests/conformance/data/schema_changes/models.test @@ -1051,3 +1051,35 @@ ALTER MODEL m1 SET OPTIONS (unknown = true); ERROR: Error parsing Spanner DDL statement: ALTER MODEL m1 SET OPTIONS (unknown = true) : Option: unknown is unknown. == +# Create model: invalid primary key in input +CREATE MODEL m +INPUT ( + f1 INT64, + f2 STRING(MAX) PRIMARY KEY +) +OUTPUT ( + l1 BOOL, + l2 ARRAY +); +-- +ERROR: Error in non-idempotent operation: Error parsing Spanner DDL statement: +CREATE MODEL m INPUT ( f1 INT64, f2 STRING(MAX) PRIMARY KEY ) OUTPUT ( l1 BOOL, +l2 ARRAY ) : Unexpected PRIMARY KEY clause +== + +# Create model: invalid primary key in output +CREATE MODEL m +INPUT ( + f1 INT64, + f2 STRING(MAX) +) +OUTPUT ( + l1 BOOL PRIMARY KEY, + l2 ARRAY +); +-- +ERROR: Error in non-idempotent operation: Error parsing Spanner DDL statement: +CREATE MODEL m INPUT ( f1 INT64, f2 STRING(MAX) ) OUTPUT ( l1 BOOL PRIMARY KEY, +l2 ARRAY ) : Unexpected PRIMARY KEY clause +== + diff --git a/tests/conformance/data/schemas/check_constraint.test b/tests/conformance/data/schemas/check_constraint.test new file mode 100644 index 00000000..6de3b715 --- /dev/null +++ b/tests/conformance/data/schemas/check_constraint.test @@ -0,0 +1,43 @@ +# Note that PG databases by default have lower case table and column names. To +# use uppercase names, you have to quote them. But the GSQL and PG quoting +# syntax is incompatible so we use lower-case names in both schemas to avoid +# having to maintain two copies of each query. +@Dialect=GOOGLE_STANDARD_SQL +CREATE TABLE t( + k INT64, + v INT64, + v_str STRING(MAX), + CONSTRAINT v_gt_zero CHECK (v > 0), + CONSTRAINT v_left_shift_gt_zero CHECK ((v << 1) > 0), + CONSTRAINT v_str_gt_zero CHECK(v_str > '0'), +) PRIMARY KEY (k); +CREATE TABLE t_gc( + k INT64, + v INT64, + g1 INT64 AS (v) STORED, + g2 INT64 AS (g1) STORED, + g3 INT64 AS (v + 5) STORED, + CONSTRAINT g1_gt_zero CHECK (g1 > 0), + CONSTRAINT g2_gt_zero CHECK(g2 > 0), + CONSTRAINT g3_gt_ten CHECK(g3 > 10), +) PRIMARY KEY (k); +=== +@Dialect=POSTGRESQL +CREATE TABLE t( + k bigint, + v bigint, + v_str varchar, + CONSTRAINT v_gt_zero CHECK (v > 0), + CONSTRAINT v_left_shift_gt_zero CHECK ((v << 1) > 0), + CONSTRAINT v_str_gt_zero CHECK(v_str > '0'), + PRIMARY KEY (k) +); +CREATE TABLE t_gc( + k bigint, + v bigint, + g1 bigint GENERATED ALWAYS AS (v) STORED, + g3 bigint GENERATED ALWAYS AS (v + 5) STORED, + CONSTRAINT g1_gt_zero CHECK (g1 > 0), + CONSTRAINT g3_gt_ten CHECK(g3 > 10), + PRIMARY KEY (k) +); diff --git a/third_party/spanner_pg/bootstrap_catalog/BUILD b/third_party/spanner_pg/bootstrap_catalog/BUILD index edbd76f8..a176cb6c 100644 --- a/third_party/spanner_pg/bootstrap_catalog/BUILD +++ b/third_party/spanner_pg/bootstrap_catalog/BUILD @@ -224,7 +224,10 @@ genrule( cc_library( name = "bootstrap_catalog_accessor_impl", srcs = ["bootstrap_catalog_accessor.cc"], - visibility = ["//third_party/spanner_pg/interface:__subpackages__"], + visibility = [ + "//third_party/spanner_pg/catalog:__subpackages__", + "//third_party/spanner_pg/interface:__subpackages__", + ], deps = [ ":bootstrap_catalog", "//third_party/spanner_pg/interface:bootstrap_catalog_accessor", diff --git a/third_party/spanner_pg/bootstrap_catalog/bootstrap_catalog_test.cc b/third_party/spanner_pg/bootstrap_catalog/bootstrap_catalog_test.cc index f3e4a73c..5a28084e 100644 --- a/third_party/spanner_pg/bootstrap_catalog/bootstrap_catalog_test.cc +++ b/third_party/spanner_pg/bootstrap_catalog/bootstrap_catalog_test.cc @@ -604,6 +604,20 @@ TEST(BootstrapCatalog, SpannerGetInternalSequenceStateProc) { EXPECT_STREQ(NameStr(procs[0]->proname), "get_internal_sequence_state"); } +TEST(BootstrapCatalog, SpannerTokenizeJSONBProc) { + ZETASQL_ASSERT_OK_AND_ASSIGN( + Oid namespace_oid, + PgBootstrapCatalog::Default()->GetNamespaceOid("spanner")); + + ZETASQL_ASSERT_OK_AND_ASSIGN( + absl::Span procs, + PgBootstrapCatalog::Default()->GetProcsByName("tokenize_jsonb")); + + ASSERT_EQ(procs.size(), 1); + EXPECT_EQ(procs[0]->pronamespace, namespace_oid); + EXPECT_STREQ(NameStr(procs[0]->proname), "tokenize_jsonb"); +} + TEST(BootstrapCatalog, SpannerGetTableColumnIdentityStateProc) { ZETASQL_ASSERT_OK_AND_ASSIGN( Oid namespace_oid, diff --git a/third_party/spanner_pg/bootstrap_catalog/spanner_pg_data/pg_proc.dat b/third_party/spanner_pg/bootstrap_catalog/spanner_pg_data/pg_proc.dat index 9260e1ab..664facdd 100644 --- a/third_party/spanner_pg/bootstrap_catalog/spanner_pg_data/pg_proc.dat +++ b/third_party/spanner_pg/bootstrap_catalog/spanner_pg_data/pg_proc.dat @@ -142,6 +142,12 @@ proargnames => '{tokens}', prosrc => 'spangres_fts_func' }, +# TOKENIZE_JSONB +{ oid => 50060, proname => 'tokenize_jsonb', pronamespace => 'spanner', + prorettype => 'tokenlist', proargtypes => 'jsonb', + proargnames => '{value}', + prosrc => 'spangres_fts_func' }, + ############################# # FTS SEARCH FUNCTIONS ############################# diff --git a/third_party/spanner_pg/catalog/BUILD b/third_party/spanner_pg/catalog/BUILD index a6f37b95..5b2151c4 100644 --- a/third_party/spanner_pg/catalog/BUILD +++ b/third_party/spanner_pg/catalog/BUILD @@ -212,6 +212,7 @@ cc_library( "//third_party/spanner_pg/util:datetime_conversion", "//third_party/spanner_pg/util:oid_to_string", "//third_party/spanner_pg/util:postgres", + "//third_party/spanner_pg/util:uuid_conversion", "@com_google_absl//absl/hash", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", @@ -222,6 +223,7 @@ cc_library( "@com_google_zetasql//zetasql/public:interval_value", "@com_google_zetasql//zetasql/public:options_cc_proto", "@com_google_zetasql//zetasql/public:type", + "@com_google_zetasql//zetasql/public:uuid_value", "@com_google_zetasql//zetasql/public:value", "@com_google_zetasql//zetasql/public/functions:date_time_util", "@com_googlesource_code_re2//:re2", @@ -259,21 +261,24 @@ cc_test( name = "type_test", srcs = ["type_test.cc"], deps = [ - ":spangres_type", ":type", "//third_party/spanner_pg/postgres_includes", "//third_party/spanner_pg/shims:error_shim", "//third_party/spanner_pg/src/backend:backend_with_shims", + "//third_party/spanner_pg/src/backend/catalog:generated_catalog_headers", "//third_party/spanner_pg/util:postgres", + "//third_party/spanner_pg/util:uuid_conversion", "//third_party/spanner_pg/util:valid_memory_context_fixture", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", "@com_google_zetasql//zetasql/base/testing:status_matchers", "@com_google_zetasql//zetasql/public:language_options", "@com_google_zetasql//zetasql/public:options_cc_proto", "@com_google_zetasql//zetasql/public:type", + "@com_google_zetasql//zetasql/public:uuid_value", "@com_google_zetasql//zetasql/public:value", "@com_google_zetasql//zetasql/resolved_ast", ], @@ -707,6 +712,7 @@ cc_library( "//backend/query:tables_from_metadata", "//backend/schema/catalog:schema", "//third_party/spanner_pg/bootstrap_catalog", + "//third_party/spanner_pg/bootstrap_catalog:bootstrap_catalog_accessor_impl", # build_cleaner: keep "//third_party/spanner_pg/catalog:engine_system_catalog", "//third_party/spanner_pg/catalog:type", "//third_party/spanner_pg/datatypes/extended:pg_oid_type", diff --git a/third_party/spanner_pg/catalog/builtin_spanner_functions.cc b/third_party/spanner_pg/catalog/builtin_spanner_functions.cc index 25888b37..e2d8496e 100644 --- a/third_party/spanner_pg/catalog/builtin_spanner_functions.cc +++ b/third_party/spanner_pg/catalog/builtin_spanner_functions.cc @@ -1094,6 +1094,13 @@ void AddSpannerFunctions(std::vector& functions) { /*mode=*/zetasql::Function::SCALAR, /*postgres_namespace=*/"spanner"}); + functions.push_back( + {"get_table_column_identity_state", + "get_table_column_identity_state", + {{{gsql_int64, {gsql_string}, /*context_ptr=*/nullptr}}}, + /*mode=*/zetasql::Function::SCALAR, + /*postgres_namespace=*/"spanner"}); + functions.push_back({"generate_uuid", "generate_uuid", {{{gsql_string, {}, /*context_ptr=*/nullptr}}}, diff --git a/third_party/spanner_pg/catalog/type.cc b/third_party/spanner_pg/catalog/type.cc index 8759cb08..3c6311e3 100644 --- a/third_party/spanner_pg/catalog/type.cc +++ b/third_party/spanner_pg/catalog/type.cc @@ -31,6 +31,7 @@ #include "third_party/spanner_pg/catalog/type.h" +#include #include #include "zetasql/base/logging.h" @@ -40,6 +41,7 @@ #include "zetasql/public/types/extended_type.h" #include "zetasql/public/types/type.h" #include "zetasql/public/types/type_factory.h" +#include "zetasql/public/uuid_value.h" #include "zetasql/base/no_destructor.h" #include "absl/hash/hash.h" #include "absl/status/status.h" @@ -55,6 +57,7 @@ #include "third_party/spanner_pg/util/datetime_conversion.h" #include "third_party/spanner_pg/util/oid_to_string.h" #include "third_party/spanner_pg/util/postgres.h" +#include "third_party/spanner_pg/util/uuid_conversion.h" #include "re2/re2.h" #include "zetasql/base/status_macros.h" @@ -976,6 +979,7 @@ const PostgresTypeMapping* PgIntervalArrayMapping() { /*requires_nan_handling=*/false); return s_pg_interval_array_mapping.get(); } + } // namespace types } // namespace postgres_translator diff --git a/third_party/spanner_pg/catalog/type.h b/third_party/spanner_pg/catalog/type.h index d459530a..e181cb8d 100644 --- a/third_party/spanner_pg/catalog/type.h +++ b/third_party/spanner_pg/catalog/type.h @@ -196,7 +196,6 @@ const PostgresTypeMapping* PgByteaArrayMapping(); const PostgresTypeMapping* PgTimestamptzArrayMapping(); const PostgresTypeMapping* PgDateArrayMapping(); const PostgresTypeMapping* PgIntervalArrayMapping(); - } // namespace types } // namespace postgres_translator diff --git a/third_party/spanner_pg/catalog/type_test.cc b/third_party/spanner_pg/catalog/type_test.cc index be2542c3..e5aa812f 100644 --- a/third_party/spanner_pg/catalog/type_test.cc +++ b/third_party/spanner_pg/catalog/type_test.cc @@ -37,6 +37,7 @@ #include "zetasql/public/options.pb.h" #include "zetasql/public/types/type.h" #include "zetasql/public/types/type_factory.h" +#include "zetasql/public/uuid_value.h" #include "zetasql/public/value.h" #include "zetasql/resolved_ast/resolved_ast.h" #include "gmock/gmock.h" @@ -44,10 +45,13 @@ #include "zetasql/base/testing/status_matchers.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/string_view.h" #include "absl/time/time.h" #include "third_party/spanner_pg/postgres_includes/all.h" #include "third_party/spanner_pg/shims/error_shim.h" +#include "third_party/spanner_pg/src/backend/catalog/pg_type_d.h" #include "third_party/spanner_pg/util/postgres.h" +#include "third_party/spanner_pg/util/uuid_conversion.h" #include "third_party/spanner_pg/util/valid_memory_context_fixture.h" #include "zetasql/base/status_macros.h" @@ -58,6 +62,21 @@ using TypeTest = ::postgres_translator::test::ValidMemoryContext; using ::zetasql_base::testing::IsOkAndHolds; using ::zetasql_base::testing::StatusIs; +// Helper function to make a PG Array Datum for creating a Const node. +// array_vals isn't const because PG doesn't understand the word, and it isn't +// an lvalue reference because the Datums *might* be moved into the output. +Datum MakePgArray(const Oid element_type, std::vector array_vals) { + // Lookup type properties from bootstrap. + int16_t elmlen; + bool elmbyval; + char elmalign; + get_typlenbyvalalign(element_type, &elmlen, &elmbyval, &elmalign); + + return PointerGetDatum(construct_array(array_vals.data(), array_vals.size(), + element_type, elmlen, elmbyval, + elmalign)); +} + // Supported Scalar Types. TEST_F(TypeTest, PgBoolMapping) { const PostgresTypeMapping* pg_bool_mapping = types::PgBoolMapping(); @@ -483,6 +502,7 @@ TEST_F(TypeTest, PgIntervalMapping) { } // Supported Array Types. + TEST_F(TypeTest, PgBoolArrayMapping) { const PostgresTypeMapping* pg_bool_array_mapping = types::PgBoolArrayMapping(); @@ -738,21 +758,6 @@ TEST_F(TypeTest, PgDateArrayMapping) { StatusIs(absl::StatusCode::kUnimplemented)); } -// Helper function to make a PG Array Datum for creating a Const node. -// array_vals isn't const because PG doesn't understand the word, and it isn't -// an lvalue reference because the Datums *might* be moved into the output. -Datum MakePgArray(const Oid element_type, std::vector array_vals) { - // Lookup type properties from bootstrap. - int16_t elmlen; - bool elmbyval; - char elmalign; - get_typlenbyvalalign(element_type, &elmlen, &elmbyval, &elmalign); - - return PointerGetDatum(construct_array(array_vals.data(), array_vals.size(), - element_type, elmlen, elmbyval, - elmalign)); -} - // Test array constant conversions (PG Const -> GSQL Value). The per-element // logic comes from element type classes, so we'll just use INT8 and test the // array-generic logic. diff --git a/third_party/spanner_pg/ddl/pg_to_spanner_ddl_translator.cc b/third_party/spanner_pg/ddl/pg_to_spanner_ddl_translator.cc index 14abe62b..1c688451 100644 --- a/third_party/spanner_pg/ddl/pg_to_spanner_ddl_translator.cc +++ b/third_party/spanner_pg/ddl/pg_to_spanner_ddl_translator.cc @@ -321,6 +321,12 @@ class PostgreSQLToSpannerDDLTranslatorImpl TableContext& context, google::spanner::emulator::backend::ddl::ColumnDefinition& out) const; absl::Status TranslateCreateDatabase(const CreatedbStmt& create_statement, google::spanner::emulator::backend::ddl::CreateDatabase& out) const; + + absl::Status TranslateAlterDatabase( + const AlterDatabaseSetStmt& alter_statement, + const TranslationOptions& options, + google::spanner::emulator::backend::ddl::AlterDatabase& out) const; + absl::StatusOr TranslateOption( absl::string_view option_name, const TranslationOptions& options) const; @@ -2394,6 +2400,78 @@ absl::Status PostgreSQLToSpannerDDLTranslatorImpl::TranslateDropSchema( return absl::OkStatus(); } +absl::Status PostgreSQLToSpannerDDLTranslatorImpl::TranslateAlterDatabase( + const AlterDatabaseSetStmt& alter_statement, + const TranslationOptions& options, google::spanner::emulator::backend::ddl::AlterDatabase& out) const { + ZETASQL_RETURN_IF_ERROR(ValidateParseTreeNode(alter_statement)); + + const VariableSetStmt* set_statement = alter_statement.setstmt; + + out.set_db_name(alter_statement.dbname); + google::spanner::emulator::backend::ddl::SetOption* opt{out.mutable_set_options()->add_options()}; + + switch (set_statement->kind) { + case VAR_SET_VALUE: { + ZETASQL_ASSIGN_OR_RETURN(internal::PGAlterOption option, + TranslateOption(set_statement->name, options)); + + opt->set_option_name(option.SpannerName()); + + ZETASQL_RET_CHECK(!IsListEmpty(set_statement->args)) + << "Option value is missing."; + + ZETASQL_ASSIGN_OR_RETURN( + const A_Const* arg, + (SingleItemListAsNode)(set_statement->args)); + + if (arg->val.node.type != option.PGType()) { + return UnsupportedTranslationError( + "Incorrect datatype specified for option."); + } + + switch (arg->val.node.type) { + case T_Integer: { + opt->set_int64_value(arg->val.ival.ival); + break; + } + + case T_String: { + opt->set_string_value(arg->val.sval.sval); + break; + } + + default: { + return UnsupportedTranslationError( + "Option type is not supported by statement."); + } + } + + break; + } + + case VAR_SET_DEFAULT: + case VAR_RESET: { + ZETASQL_ASSIGN_OR_RETURN(internal::PGAlterOption option, + TranslateOption(set_statement->name, options)); + + opt->set_option_name(option.SpannerName()); + + opt->set_null_value(true); + ZETASQL_RET_CHECK(IsListEmpty(set_statement->args)); + break; + } + + case VAR_SET_CURRENT: + case VAR_SET_MULTI: + case VAR_RESET_ALL: { + return UnsupportedTranslationError( + "Operation is not supported by statement."); + } + } + + return absl::OkStatus(); +} + absl::Status PostgreSQLToSpannerDDLTranslatorImpl::TranslateVacuum( const VacuumStmt& vacuum_statement, const TranslationOptions& options, google::spanner::emulator::backend::ddl::Analyze& out) const { @@ -2804,6 +2882,17 @@ absl::Status PostgreSQLToSpannerDDLTranslatorImpl::Visitor::Visit( break; } + case T_AlterDatabaseSetStmt: { + ZETASQL_ASSIGN_OR_RETURN( + const AlterDatabaseSetStmt* statement, + (DowncastNode( + raw_statement.stmt))); + ZETASQL_RETURN_IF_ERROR(ddl_translator_.TranslateAlterDatabase( + *statement, options_, *result_statement.mutable_alter_database())); + + break; + } + case T_IndexStmt: { ZETASQL_ASSIGN_OR_RETURN( const IndexStmt* statement, diff --git a/third_party/spanner_pg/ddl/spangres_direct_schema_printer_impl.cc b/third_party/spanner_pg/ddl/spangres_direct_schema_printer_impl.cc index 958fc3b8..af36e83e 100644 --- a/third_party/spanner_pg/ddl/spangres_direct_schema_printer_impl.cc +++ b/third_party/spanner_pg/ddl/spangres_direct_schema_printer_impl.cc @@ -132,6 +132,8 @@ class SpangresSchemaPrinterImpl : public SpangresSchemaPrinter { // corresponding PostgreSQL DDL representation absl::StatusOr> PrintCreateDatabase( const google::spanner::emulator::backend::ddl::CreateDatabase& statement) const; + absl::StatusOr> PrintAlterDatabase( + const google::spanner::emulator::backend::ddl::AlterDatabase& statement) const; absl::StatusOr PrintAlterTable( const google::spanner::emulator::backend::ddl::AlterTable& statement) const; absl::StatusOr PrintCreateTable( @@ -178,6 +180,9 @@ class SpangresSchemaPrinterImpl : public SpangresSchemaPrinter { absl::StatusOr PrintCheckConstraint( const CheckConstraint& check_constraint) const; absl::StatusOr PrintSortOrder(KeyPartClause::Order order) const; + absl::StatusOr> PrintAlterDatabaseSetOptions( + absl::string_view quoted_db_name, + const google::protobuf::RepeatedPtrField& options) const; std::string PrintCreateChangeStream( const google::spanner::emulator::backend::ddl::CreateChangeStream& statement) const; std::string PrintChangeStreamForClause( @@ -234,6 +239,8 @@ SpangresSchemaPrinterImpl::PrintDDLStatement( switch (statement.statement_case()) { case google::spanner::emulator::backend::ddl::DDLStatement::kCreateDatabase: return PrintCreateDatabase(statement.create_database()); + case google::spanner::emulator::backend::ddl::DDLStatement::kAlterDatabase: + return PrintAlterDatabase(statement.alter_database()); case google::spanner::emulator::backend::ddl::DDLStatement::kAlterTable: return WrapOutput(PrintAlterTable(statement.alter_table())); case google::spanner::emulator::backend::ddl::DDLStatement::kCreateTable: @@ -311,6 +318,16 @@ SpangresSchemaPrinterImpl::PrintCreateDatabase( return output; } +absl::StatusOr> +SpangresSchemaPrinterImpl::PrintAlterDatabase( + const google::spanner::emulator::backend::ddl::AlterDatabase& statement) const { + ZETASQL_RET_CHECK_EQ(statement.alter_type_case(), + google::spanner::emulator::backend::ddl::AlterDatabase::kSetOptions); + + return PrintAlterDatabaseSetOptions(QuoteIdentifier(statement.db_name()), + statement.set_options().options()); +} + absl::StatusOr SpangresSchemaPrinterImpl::PrintAlterTable( const google::spanner::emulator::backend::ddl::AlterTable& statement) const { std::string alter_table = @@ -1315,6 +1332,42 @@ absl::StatusOr SpangresSchemaPrinterImpl::PrintPrimaryKey( StrAppend(&output, ")"); return output; } + +absl::StatusOr> +SpangresSchemaPrinterImpl::PrintAlterDatabaseSetOptions( + absl::string_view quoted_db_name, + const google::protobuf::RepeatedPtrField& options) const { + std::vector output; + std::string base = StrCat("ALTER DATABASE ", quoted_db_name); + std::vector printed_options; + + for (const google::spanner::emulator::backend::ddl::SetOption& option : options) { + absl::optional pg_option_name = + internal::GetSpangresOptionName(option.option_name()); + // Options not whitelisted for customer use are not printed + if (!pg_option_name) { + continue; + } + if (option.has_null_value()) { + output.push_back( + StrCat(base, " RESET ", QuoteIdentifier(*pg_option_name))); + continue; + } else { + std::string value; + if (option.has_string_value()) { + value = QuoteStringLiteral(option.string_value()); + } else if (option.has_int64_value()) { + value = std::to_string(option.int64_value()); + } else { + ZETASQL_RET_CHECK_FAIL(); + } + output.push_back(StrCat(base, " SET ", QuoteIdentifier(*pg_option_name), + " = ", value)); + } + } + return output; +} + } // namespace absl::StatusOr> diff --git a/third_party/spanner_pg/errors/error_catalog.textproto b/third_party/spanner_pg/errors/error_catalog.textproto index 5f46bd78..f08dd08d 100644 --- a/third_party/spanner_pg/errors/error_catalog.textproto +++ b/third_party/spanner_pg/errors/error_catalog.textproto @@ -35,6 +35,11 @@ group { name: "InvalidLimitSyntax" pg_message_id: "LIMIT #,# syntax is not supported" } + + message { + name: "InvalidDefaultSyntax" + pg_message_id: "DEFAULT is not allowed in this context" + } } group { diff --git a/third_party/spanner_pg/interface/BUILD b/third_party/spanner_pg/interface/BUILD index 15b4a293..1f7e1390 100644 --- a/third_party/spanner_pg/interface/BUILD +++ b/third_party/spanner_pg/interface/BUILD @@ -80,7 +80,6 @@ cc_library( licenses = ["unencumbered"], visibility = ["//:__subpackages__"], deps = [ - ":bootstrap_catalog_accessor_stub", ":bootstrap_catalog_data_cc_proto", ":memory_reservation_manager", ":parser_interface", @@ -455,18 +454,6 @@ cc_library( ], ) -cc_library( - name = "bootstrap_catalog_accessor_stub", - srcs = ["stub_bootstrap_catalog_accessor.cc"], - visibility = ["//visibility:private"], - deps = [ - ":bootstrap_catalog_accessor", - ":bootstrap_catalog_data_cc_proto", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - ], -) - cc_library( name = "emulator_builtin_function_catalog", hdrs = [ @@ -493,6 +480,7 @@ cc_library( ":interfaces_with_stub_implementations", ":parser_interface", "//backend/query:function_catalog", + "//third_party/spanner_pg/bootstrap_catalog:bootstrap_catalog_accessor_impl", "//third_party/spanner_pg/shims:error_shim", "//third_party/spanner_pg/src/backend:backend_with_shims", "@com_google_absl//absl/status", diff --git a/third_party/spanner_pg/postgres_includes/pg-include-list.h b/third_party/spanner_pg/postgres_includes/pg-include-list.h index ab595ca4..9542569f 100644 --- a/third_party/spanner_pg/postgres_includes/pg-include-list.h +++ b/third_party/spanner_pg/postgres_includes/pg-include-list.h @@ -121,6 +121,7 @@ #include "third_party/spanner_pg/src/include/utils/syscache.h" #include "third_party/spanner_pg/src/include/utils/timestamp.h" #include "third_party/spanner_pg/src/include/utils/typcache.h" +#include "third_party/spanner_pg/src/include/utils/uuid.h" #include "third_party/spanner_pg/src/include/mb/pg_wchar.h" // clang-format on diff --git a/third_party/spanner_pg/shims/BUILD b/third_party/spanner_pg/shims/BUILD index 52fcd54e..1165353c 100644 --- a/third_party/spanner_pg/shims/BUILD +++ b/third_party/spanner_pg/shims/BUILD @@ -487,15 +487,9 @@ cc_library( name = "catalog_shim", srcs = [ "catalog_shim.c", - "catalog_shim_acl.c", "catalog_shim_analyze.c", - "catalog_shim_catcache.c", - "catalog_shim_date.c", - "catalog_shim_datetime.c", - "catalog_shim_dbcommands.c", "catalog_shim_deparser.c", "catalog_shim_expected_errors.c", - "catalog_shim_fmgr.c", "catalog_shim_format_type.c", "catalog_shim_funcapi.c", "catalog_shim_indexcmds.c", @@ -510,10 +504,7 @@ cc_library( "catalog_shim_parse_relation.c", "catalog_shim_parse_target.c", "catalog_shim_parse_type.c", - "catalog_shim_pg_constraint.c", - "catalog_shim_rewriteHandler.c", "catalog_shim_ruleutils.c", - "catalog_shim_timestamp.c", "catalog_shim_tokenlist.c", "catalog_shim_tupdesc.c", "catalog_shim_typcache.c", diff --git a/third_party/spanner_pg/shims/catalog_shim.h b/third_party/spanner_pg/shims/catalog_shim.h index 17cc734f..6bf77218 100644 --- a/third_party/spanner_pg/shims/catalog_shim.h +++ b/third_party/spanner_pg/shims/catalog_shim.h @@ -1107,17 +1107,6 @@ TypeCacheEntry* lookup_type_cache(Oid type_id, int flags); // identify this cache entry to lookup_rowtype_tupdesc. void assign_record_type_typmod(TupleDesc tupDesc); -/*---------------------------------------------------------------------------*/ -/* Shimmed functions from utils/fmgr/fmgr.c */ - -// Populates FmgrInfo with function call information (based on Oid lookup). -// Spangres version is exactly the same for built-in functions, but doesn't -// support non-built-in functions at all. The PostgreSQL version falls back to a -// lookup in the pg_proc table. Since we don't have a pg_proc table (just -// bootstrap catalog), this doesn't miss anything. -void fmgr_info_cxt_security(Oid functionId, FmgrInfo* finfo, MemoryContext mcxt, - bool ignore_security); - /*---------------------------------------------------------------------------*/ /* Shimmed functions from utils/fmgr/funcapi.c */ diff --git a/third_party/spanner_pg/shims/catalog_shim_acl.c b/third_party/spanner_pg/shims/catalog_shim_acl.c deleted file mode 100644 index 3d6c74aa..00000000 --- a/third_party/spanner_pg/shims/catalog_shim_acl.c +++ /dev/null @@ -1,86 +0,0 @@ -// -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -// Portions derived from source copyright Postgres Global Development Group in -// accordance with the following notice: -//------------------------------------------------------------------------------ -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2022 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -#include "third_party/spanner_pg/shims/catalog_shim.h" - -#include "third_party/spanner_pg/postgres_includes/all.h" - -/* - * get_role_oid - Given a role name, look up the role's OID. - * - * If missing_ok is false, throw an error if role name not found. If - * true, just return InvalidOid. - * - * SPANGRES: Currently, we do not support roles so we always return either - * InvalidOid or throw an error. This will need to be updated when we add - * support for roles. - */ -Oid get_role_oid(const char *rolname, bool missing_ok) -{ - if (!missing_ok) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("role \"%s\" does not exist", rolname))); - return InvalidOid; -} diff --git a/third_party/spanner_pg/shims/catalog_shim_catcache.c b/third_party/spanner_pg/shims/catalog_shim_catcache.c deleted file mode 100644 index ba8a8d75..00000000 --- a/third_party/spanner_pg/shims/catalog_shim_catcache.c +++ /dev/null @@ -1,70 +0,0 @@ -// -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -// Portions derived from source copyright Postgres Global Development Group in -// accordance with the following notice: -//------------------------------------------------------------------------------ -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2022 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -#include "third_party/spanner_pg/shims/catalog_shim.h" - -void ReleaseCatCache(HeapTuple tuple) { - // Do nothing. -} - diff --git a/third_party/spanner_pg/shims/catalog_shim_date.c b/third_party/spanner_pg/shims/catalog_shim_date.c deleted file mode 100644 index 363288f6..00000000 --- a/third_party/spanner_pg/shims/catalog_shim_date.c +++ /dev/null @@ -1,133 +0,0 @@ -// -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -// Portions derived from source copyright Postgres Global Development Group in -// accordance with the following notice: -//------------------------------------------------------------------------------ -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -#include "third_party/spanner_pg/shims/catalog_shim.h" - -#include "third_party/spanner_pg/postgres_includes/all.h" - -/* date_in() - * Given date text string, convert to internal date format. - */ -Datum -date_in(PG_FUNCTION_ARGS) -{ - char *str = PG_GETARG_CSTRING(0); - DateADT date; - fsec_t fsec; - struct pg_tm tt, - *tm = &tt; - int tzp; - int dtype; - int nf; - int dterr; - char *field[MAXDATEFIELDS]; - int ftype[MAXDATEFIELDS]; - char workbuf[MAXDATELEN + 1]; - - dterr = ParseDateTime(str, workbuf, sizeof(workbuf), - field, ftype, MAXDATEFIELDS, &nf); - if (dterr == 0) - dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp); - if (dterr != 0) - DateTimeParseError(dterr, str, "date"); - - switch (dtype) - { - case DTK_DATE: - break; - - case DTK_EPOCH: - /* SPANGRES: remove support for epoch */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("epoch is not supported"))); - - case DTK_LATE: - case DTK_EARLY: - /* SPANGRES: remove support for infinity and -infinity */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("infinity and -infinity are not supported"))); - - default: - DateTimeParseError(DTERR_BAD_FORMAT, str, "date"); - break; - } - - /* Prevent overflow in Julian-day routines */ - if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("date out of range: \"%s\"", str))); - - date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; - - /* Now check for just-out-of-range dates */ - if (!IS_VALID_DATE(date)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("date out of range: \"%s\"", str))); - - PG_RETURN_DATEADT(date); -} diff --git a/third_party/spanner_pg/shims/catalog_shim_datetime.c b/third_party/spanner_pg/shims/catalog_shim_datetime.c deleted file mode 100644 index 62330e75..00000000 --- a/third_party/spanner_pg/shims/catalog_shim_datetime.c +++ /dev/null @@ -1,806 +0,0 @@ -// -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -// Portions derived from source copyright Postgres Global Development Group in -// accordance with the following notice: -//------------------------------------------------------------------------------ -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -#include "third_party/spanner_pg/shims/catalog_shim.h" - -#include "third_party/spanner_pg/postgres_includes/all.h" -#include "third_party/spanner_pg/shims/catalog_shim_cc_wrappers.h" - -// We made these functions non-static to call from here. Forward declare them to -// limit the scope. -int DecodeNumber(int flen, char *field, bool haveTextMonth, - int fmask, int *tmask, - struct pg_tm *tm, fsec_t *fsec, bool *is2digits); -int DecodeNumberField(int len, char *str, - int fmask, int *tmask, - struct pg_tm *tm, fsec_t *fsec, bool *is2digits); -int DecodeTime(char *str, int fmask, int range, - int *tmask, struct pg_tm *tm, fsec_t *fsec); -const datetkn *datebsearch(const char *key, const datetkn *base, int nel); -int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, - struct pg_tm *tm); -int -ParseFractionalSecond(char *cp, fsec_t *fsec); - -static __thread const datetkn *abbrevcache[MAXDATEFIELDS] = {NULL}; - -/* DecodeDateTime() - * Interpret previously parsed fields for general date and time. - * Return 0 if full date, 1 if only time, and negative DTERR code if problems. - * (Currently, all callers treat 1 as an error return too.) - * - * External format(s): - * " -- ::" - * "Fri Feb-7-1997 15:23:27" - * "Feb-7-1997 15:23:27" - * "2-7-1997 15:23:27" - * "1997-2-7 15:23:27" - * "1997.038 15:23:27" (day of year 1-366) - * Also supports input in compact time: - * "970207 152327" - * "97038 152327" - * "20011225T040506.789-07" - * - * Use the system-provided functions to get the current time zone - * if not specified in the input string. - * - * If the date is outside the range of pg_time_t (in practice that could only - * happen if pg_time_t is just 32 bits), then assume UTC time zone - thomas - * 1997-05-27 - */ -int -DecodeDateTime(char **field, int *ftype, int nf, - int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp) -{ - int fmask = 0, - tmask, - type; - int ptype = 0; /* "prefix type" for ISO y2001m02d04 format */ - int i; - int val; - int dterr; - int mer = HR24; - bool haveTextMonth = false; - bool isjulian = false; - bool is2digits = false; - bool bc = false; - pg_tz *namedTz = NULL; - pg_tz *abbrevTz = NULL; - pg_tz *valtz; - char *abbrev = NULL; - - /* - * We'll insist on at least all of the date fields, but initialize the - * remaining fields in case they are not set later... - */ - *dtype = DTK_DATE; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - *fsec = 0; - /* don't know daylight savings time status apriori */ - tm->tm_isdst = -1; - if (tzp != NULL) - *tzp = 0; - - for (i = 0; i < nf; i++) - { - switch (ftype[i]) - { - case DTK_DATE: - - /* - * Integral julian day with attached time zone? All other - * forms with JD will be separated into distinct fields, so we - * handle just this case here. - */ - if (ptype == DTK_JULIAN) - { - char *cp; - int val; - - if (tzp == NULL) - return DTERR_BAD_FORMAT; - - errno = 0; - val = strtoint(field[i], &cp, 10); - if (errno == ERANGE || val < 0) - return DTERR_FIELD_OVERFLOW; - - j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - isjulian = true; - - /* Get the time zone from the end of the string */ - dterr = DecodeTimezone(cp, tzp); - if (dterr) - return dterr; - - tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ); - ptype = 0; - break; - } - - /* - * Already have a date? Then this might be a time zone name - * with embedded punctuation (e.g. "America/New_York") or a - * run-together time with trailing time zone (e.g. hhmmss-zz). - * - thomas 2001-12-25 - * - * We consider it a time zone if we already have month & day. - * This is to allow the form "mmm dd hhmmss tz year", which - * we've historically accepted. - */ - else if (ptype != 0 || - ((fmask & (DTK_M(MONTH) | DTK_M(DAY))) == - (DTK_M(MONTH) | DTK_M(DAY)))) - { - /* No time zone accepted? Then quit... */ - if (tzp == NULL) - return DTERR_BAD_FORMAT; - - if (isdigit((unsigned char) *field[i]) || ptype != 0) - { - char *cp; - - if (ptype != 0) - { - /* Validity check; should not fail this test */ - if (ptype != DTK_TIME) - return DTERR_BAD_FORMAT; - ptype = 0; - } - - /* - * Starts with a digit but we already have a time - * field? Then we are in trouble with a date and time - * already... - */ - if ((fmask & DTK_TIME_M) == DTK_TIME_M) - return DTERR_BAD_FORMAT; - - if ((cp = strchr(field[i], '-')) == NULL) - return DTERR_BAD_FORMAT; - - /* Get the time zone from the end of the string */ - dterr = DecodeTimezone(cp, tzp); - if (dterr) - return dterr; - *cp = '\0'; - - /* - * Then read the rest of the field as a concatenated - * time - */ - dterr = DecodeNumberField(strlen(field[i]), field[i], - fmask, - &tmask, tm, - fsec, &is2digits); - if (dterr < 0) - return dterr; - - /* - * modify tmask after returning from - * DecodeNumberField() - */ - tmask |= DTK_M(TZ); - } - else - { - namedTz = pg_tzset(field[i]); - if (!namedTz) - { - /* - * We should return an error code instead of - * ereport'ing directly, but then there is no way - * to report the bad time zone name. - */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" not recognized", - field[i]))); - } - /* we'll apply the zone setting below */ - tmask = DTK_M(TZ); - } - } - else - { - dterr = DecodeDate(field[i], fmask, - &tmask, &is2digits, tm); - if (dterr) - return dterr; - } - break; - - case DTK_TIME: - - /* - * This might be an ISO time following a "t" field. - */ - if (ptype != 0) - { - /* Validity check; should not fail this test */ - if (ptype != DTK_TIME) - return DTERR_BAD_FORMAT; - ptype = 0; - } - dterr = DecodeTime(field[i], fmask, INTERVAL_FULL_RANGE, - &tmask, tm, fsec); - if (dterr) - return dterr; - - /* check for time overflow */ - if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec, - *fsec)) - return DTERR_FIELD_OVERFLOW; - break; - - case DTK_TZ: - { - int tz; - - if (tzp == NULL) - return DTERR_BAD_FORMAT; - - dterr = DecodeTimezone(field[i], &tz); - if (dterr) - return dterr; - *tzp = tz; - tmask = DTK_M(TZ); - } - break; - - case DTK_NUMBER: - - /* - * Was this an "ISO date" with embedded field labels? An - * example is "y2001m02d04" - thomas 2001-02-04 - */ - if (ptype != 0) - { - char *cp; - int val; - - errno = 0; - val = strtoint(field[i], &cp, 10); - if (errno == ERANGE) - return DTERR_FIELD_OVERFLOW; - - /* - * only a few kinds are allowed to have an embedded - * decimal - */ - if (*cp == '.') - switch (ptype) - { - case DTK_JULIAN: - case DTK_TIME: - case DTK_SECOND: - break; - default: - return DTERR_BAD_FORMAT; - break; - } - else if (*cp != '\0') - return DTERR_BAD_FORMAT; - - switch (ptype) - { - case DTK_YEAR: - tm->tm_year = val; - tmask = DTK_M(YEAR); - break; - - case DTK_MONTH: - - /* - * already have a month and hour? then assume - * minutes - */ - if ((fmask & DTK_M(MONTH)) != 0 && - (fmask & DTK_M(HOUR)) != 0) - { - tm->tm_min = val; - tmask = DTK_M(MINUTE); - } - else - { - tm->tm_mon = val; - tmask = DTK_M(MONTH); - } - break; - - case DTK_DAY: - tm->tm_mday = val; - tmask = DTK_M(DAY); - break; - - case DTK_HOUR: - tm->tm_hour = val; - tmask = DTK_M(HOUR); - break; - - case DTK_MINUTE: - tm->tm_min = val; - tmask = DTK_M(MINUTE); - break; - - case DTK_SECOND: - tm->tm_sec = val; - tmask = DTK_M(SECOND); - if (*cp == '.') - { - dterr = ParseFractionalSecond(cp, fsec); - if (dterr) - return dterr; - tmask = DTK_ALL_SECS_M; - } - break; - - case DTK_TZ: - tmask = DTK_M(TZ); - dterr = DecodeTimezone(field[i], tzp); - if (dterr) - return dterr; - break; - - case DTK_JULIAN: - /* previous field was a label for "julian date" */ - if (val < 0) - return DTERR_FIELD_OVERFLOW; - tmask = DTK_DATE_M; - j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - isjulian = true; - - /* fractional Julian Day? */ - if (*cp == '.') - { - double time; - - errno = 0; - time = strtod(cp, &cp); - if (*cp != '\0' || errno != 0) - return DTERR_BAD_FORMAT; - time *= USECS_PER_DAY; - dt2time(time, - &tm->tm_hour, &tm->tm_min, - &tm->tm_sec, fsec); - tmask |= DTK_TIME_M; - } - break; - - case DTK_TIME: - /* previous field was "t" for ISO time */ - dterr = DecodeNumberField(strlen(field[i]), field[i], - (fmask | DTK_DATE_M), - &tmask, tm, - fsec, &is2digits); - if (dterr < 0) - return dterr; - if (tmask != DTK_TIME_M) - return DTERR_BAD_FORMAT; - break; - - default: - return DTERR_BAD_FORMAT; - break; - } - - ptype = 0; - *dtype = DTK_DATE; - } - else - { - char *cp; - int flen; - - flen = strlen(field[i]); - cp = strchr(field[i], '.'); - - /* Embedded decimal and no date yet? */ - if (cp != NULL && !(fmask & DTK_DATE_M)) - { - dterr = DecodeDate(field[i], fmask, - &tmask, &is2digits, tm); - if (dterr) - return dterr; - } - /* embedded decimal and several digits before? */ - else if (cp != NULL && flen - strlen(cp) > 2) - { - /* - * Interpret as a concatenated date or time Set the - * type field to allow decoding other fields later. - * Example: 20011223 or 040506 - */ - dterr = DecodeNumberField(flen, field[i], fmask, - &tmask, tm, - fsec, &is2digits); - if (dterr < 0) - return dterr; - } - - /* - * Is this a YMD or HMS specification, or a year number? - * YMD and HMS are required to be six digits or more, so - * if it is 5 digits, it is a year. If it is six or more - * digits, we assume it is YMD or HMS unless no date and - * no time values have been specified. This forces 6+ - * digit years to be at the end of the string, or to use - * the ISO date specification. - */ - else if (flen >= 6 && (!(fmask & DTK_DATE_M) || - !(fmask & DTK_TIME_M))) - { - dterr = DecodeNumberField(flen, field[i], fmask, - &tmask, tm, - fsec, &is2digits); - if (dterr < 0) - return dterr; - } - /* otherwise it is a single date/time field... */ - else - { - dterr = DecodeNumber(flen, field[i], - haveTextMonth, fmask, - &tmask, tm, - fsec, &is2digits); - if (dterr) - return dterr; - } - } - break; - - case DTK_STRING: - case DTK_SPECIAL: - /* timezone abbrevs take precedence over built-in tokens */ - type = DecodeTimezoneAbbrev(i, field[i], &val, &valtz); - if (type == UNKNOWN_FIELD) - type = DecodeSpecial(i, field[i], &val); - if (type == IGNORE_DTF) - continue; - - // SPANGRES: do not call DTK_M(UNKNOWN_FIELD), which is 1 << 31 - // and undefined behavior. For b/300504021. - // If the type is UNKNOWN_FIELD, the code below will either - // return an error or set tmask to DTK_M(TZ). It does not rely - // on tmask being set here. - if (type != UNKNOWN_FIELD) - { - tmask = DTK_M(type); - } - switch (type) - { - case RESERV: - switch (val) - { - case DTK_NOW: - case DTK_YESTERDAY: - case DTK_TODAY: - case DTK_TOMORROW: - /* SPANGRES: return an error for relative timestamps */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("current/relative datetimes are not supported"))); - - case DTK_ZULU: - tmask = (DTK_TIME_M | DTK_M(TZ)); - *dtype = DTK_DATE; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - if (tzp != NULL) - *tzp = 0; - break; - - default: - *dtype = val; - } - - break; - - case MONTH: - - /* - * already have a (numeric) month? then see if we can - * substitute... - */ - if ((fmask & DTK_M(MONTH)) && !haveTextMonth && - !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 && - tm->tm_mon <= 31) - { - tm->tm_mday = tm->tm_mon; - tmask = DTK_M(DAY); - } - haveTextMonth = true; - tm->tm_mon = val; - break; - - case DTZMOD: - - /* - * daylight savings time modifier (solves "MET DST" - * syntax) - */ - tmask |= DTK_M(DTZ); - tm->tm_isdst = 1; - if (tzp == NULL) - return DTERR_BAD_FORMAT; - *tzp -= val; - break; - - case DTZ: - - /* - * set mask for TZ here _or_ check for DTZ later when - * getting default timezone - */ - tmask |= DTK_M(TZ); - tm->tm_isdst = 1; - if (tzp == NULL) - return DTERR_BAD_FORMAT; - *tzp = -val; - break; - - case TZ: - tm->tm_isdst = 0; - if (tzp == NULL) - return DTERR_BAD_FORMAT; - *tzp = -val; - break; - - case DYNTZ: - tmask |= DTK_M(TZ); - if (tzp == NULL) - return DTERR_BAD_FORMAT; - /* we'll determine the actual offset later */ - abbrevTz = valtz; - abbrev = field[i]; - break; - - case AMPM: - mer = val; - break; - - case ADBC: - bc = (val == BC); - break; - - case DOW: - tm->tm_wday = val; - break; - - case UNITS: - tmask = 0; - ptype = val; - break; - - case ISOTIME: - - /* - * This is a filler field "t" indicating that the next - * field is time. Try to verify that this is sensible. - */ - tmask = 0; - - /* No preceding date? Then quit... */ - if ((fmask & DTK_DATE_M) != DTK_DATE_M) - return DTERR_BAD_FORMAT; - - /*** - * We will need one of the following fields: - * DTK_NUMBER should be hhmmss.fff - * DTK_TIME should be hh:mm:ss.fff - * DTK_DATE should be hhmmss-zz - ***/ - if (i >= nf - 1 || - (ftype[i + 1] != DTK_NUMBER && - ftype[i + 1] != DTK_TIME && - ftype[i + 1] != DTK_DATE)) - return DTERR_BAD_FORMAT; - - ptype = val; - break; - - case UNKNOWN_FIELD: - - /* - * Before giving up and declaring error, check to see - * if it is an all-alpha timezone name. - */ - namedTz = pg_tzset(field[i]); - if (!namedTz) - return DTERR_BAD_FORMAT; - /* we'll apply the zone setting below */ - tmask = DTK_M(TZ); - break; - - default: - return DTERR_BAD_FORMAT; - } - break; - - default: - return DTERR_BAD_FORMAT; - } - - if (tmask & fmask) - return DTERR_BAD_FORMAT; - fmask |= tmask; - } /* end loop over fields */ - - /* do final checking/adjustment of Y/M/D fields */ - dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm); - if (dterr) - return dterr; - - /* handle AM/PM */ - if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2) - return DTERR_FIELD_OVERFLOW; - if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2) - tm->tm_hour = 0; - else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2) - tm->tm_hour += HOURS_PER_DAY / 2; - - /* do additional checking for full date specs... */ - if (*dtype == DTK_DATE) - { - if ((fmask & DTK_DATE_M) != DTK_DATE_M) - { - if ((fmask & DTK_TIME_M) == DTK_TIME_M) - return 1; - return DTERR_BAD_FORMAT; - } - - /* - * If we had a full timezone spec, compute the offset (we could not do - * it before, because we need the date to resolve DST status). - */ - if (namedTz != NULL) - { - /* daylight savings time modifier disallowed with full TZ */ - if (fmask & DTK_M(DTZMOD)) - return DTERR_BAD_FORMAT; - - *tzp = DetermineTimeZoneOffset(tm, namedTz); - } - - /* - * Likewise, if we had a dynamic timezone abbreviation, resolve it - * now. - */ - if (abbrevTz != NULL) - { - /* daylight savings time modifier disallowed with dynamic TZ */ - if (fmask & DTK_M(DTZMOD)) - return DTERR_BAD_FORMAT; - - *tzp = DetermineTimeZoneAbbrevOffset(tm, abbrev, abbrevTz); - } - - if (tzp != NULL && !(fmask & DTK_M(TZ))) - { - /* - * daylight savings time modifier but no standard timezone? then - * error - */ - if (fmask & DTK_M(DTZMOD)) - return DTERR_BAD_FORMAT; - - *tzp = DetermineTimeZoneOffset(tm, session_timezone); - } - } - - return 0; -} - -/* DecodeTimezoneAbbrev() - * Interpret string as a timezone abbreviation, if possible. - * - * Returns an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if - * string is not any known abbreviation. On success, set *offset and *tz to - * represent the UTC offset (for TZ or DTZ) or underlying zone (for DYNTZ). - * Note that full timezone names (such as America/New_York) are not handled - * here, mostly for historical reasons. - * - * Given string must be lowercased already. - * - * Implement a cache lookup since it is likely that dates - * will be related in format. - */ -int -DecodeTimezoneAbbrev(int field, char *lowtoken, - int *offset, pg_tz **tz) -{ - int type; - const datetkn *tp; - - tp = abbrevcache[field]; - /* use strncmp so that we match truncated tokens */ - /* - * SPANGRES: use the hardcoded SpangresTimezoneAbbrevTable instead of a zoneabbrevtbl - * loaded from a file - */ - if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0) - { - tp = datebsearch(lowtoken, SpangresTimezoneAbbrevTable, SpangresTimezoneAbbrevTableSize); - } - if (tp == NULL) - { - type = UNKNOWN_FIELD; - *offset = 0; - *tz = NULL; - } - else - { - abbrevcache[field] = tp; - type = tp->type; - /* SPANGRES: dynamic timezone abbreviations are not supported*/ - Assert(type != DYNTZ); - *offset = tp->value; - *tz = NULL; - } - - return type; -} diff --git a/third_party/spanner_pg/shims/catalog_shim_dbcommands.c b/third_party/spanner_pg/shims/catalog_shim_dbcommands.c deleted file mode 100644 index a8783f79..00000000 --- a/third_party/spanner_pg/shims/catalog_shim_dbcommands.c +++ /dev/null @@ -1,80 +0,0 @@ -// -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -// Portions derived from source copyright Postgres Global Development Group in -// accordance with the following notice: -//------------------------------------------------------------------------------ -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2022 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -#include "third_party/spanner_pg/shims/catalog_shim.h" - -#include "third_party/spanner_pg/postgres_includes/all.h" - -Oid get_database_oid(const char* dbname, bool missing_ok) { - // TODO: Add support for namespace Oids. - if (missing_ok) { - return InvalidOid; - } else { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("Tried to lookup Oid for database %s, but " - "using databases as queryable namespaces is not supported", - dbname))); - } -} diff --git a/third_party/spanner_pg/shims/catalog_shim_expected_errors.c b/third_party/spanner_pg/shims/catalog_shim_expected_errors.c index 7f1c0552..fd866937 100644 --- a/third_party/spanner_pg/shims/catalog_shim_expected_errors.c +++ b/third_party/spanner_pg/shims/catalog_shim_expected_errors.c @@ -79,26 +79,6 @@ // There is an expected_errors_test suite to exercise these code paths and // ensure they don't lead to crashes. -LockAcquireResult LockAcquireExtended(const LOCKTAG* locktag, - LOCKMODE lockmode, - bool sessionLock, bool dontWait, - bool reportMemoryError, - LOCALLOCK** locallockp) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("Attempted an unsupported operation in the PostgreSQL " - "locking subsystem"))); -} - -// Guarantee that `guc_variables` is not built so that we can treat -// GUC-controlled variables as simple globals for evaluating their access -// patterns and thread safety. -void InitializeGUCOptions(void) { - ereport( - ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("PostgreSQL's configuration system (GUC) is not supported."))); -} - // We don't maintain a pg_class entry for user tables, so we can't provide a // pg_type to describe the row. We can at least give as clear an error as // possible. diff --git a/third_party/spanner_pg/shims/catalog_shim_fmgr.c b/third_party/spanner_pg/shims/catalog_shim_fmgr.c deleted file mode 100644 index a61857dd..00000000 --- a/third_party/spanner_pg/shims/catalog_shim_fmgr.c +++ /dev/null @@ -1,104 +0,0 @@ -// -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -// Portions derived from source copyright Postgres Global Development Group in -// accordance with the following notice: -//------------------------------------------------------------------------------ -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2022 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -#include "third_party/spanner_pg/shims/catalog_shim.h" - -#include "third_party/spanner_pg/postgres_includes/all.h" - -// We gave this normally-static function external linkage so that our -// shimmed-out fmgr_info_cxt_security below could use it. Forward -// declared here to limit the scope. -const FmgrBuiltin* fmgr_isbuiltin(Oid id); - -void fmgr_info_cxt_security(Oid functionId, FmgrInfo* finfo, - MemoryContext mcxt, bool ignore_security) { - const FmgrBuiltin* fbp; - - /* - * fn_oid *must* be filled in last. Some code assumes that if fn_oid is - * valid, the whole struct is valid. Some FmgrInfo struct's do survive - * elogs. - */ - finfo->fn_oid = InvalidOid; - finfo->fn_extra = NULL; - finfo->fn_mcxt = mcxt; - finfo->fn_expr = NULL; // caller may set this later - - if ((fbp = fmgr_isbuiltin(functionId)) != NULL) { - /* - * Fast path for builtin functions: don't bother consulting pg_proc - */ - finfo->fn_nargs = fbp->nargs; - finfo->fn_strict = fbp->strict; - finfo->fn_retset = fbp->retset; - finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ - finfo->fn_addr = fbp->func; - finfo->fn_oid = functionId; - return; - } - - ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), - errmsg("Unknown function or function is not a built-in: %u", - functionId))); -} diff --git a/third_party/spanner_pg/shims/catalog_shim_pg_constraint.c b/third_party/spanner_pg/shims/catalog_shim_pg_constraint.c deleted file mode 100644 index ae6ae9da..00000000 --- a/third_party/spanner_pg/shims/catalog_shim_pg_constraint.c +++ /dev/null @@ -1,75 +0,0 @@ -// -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -// Portions derived from source copyright Postgres Global Development Group in -// accordance with the following notice: -//------------------------------------------------------------------------------ -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2022 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -#include "third_party/spanner_pg/shims/catalog_shim.h" - -#include "third_party/spanner_pg/postgres_includes/all.h" - -Bitmapset* get_primary_key_attnos(Oid relid, bool deferrableOk, - Oid* constraintOid) { - // SPANGRES: this feature is unsupported. Return null set. - // TODO: Add support for primary key lookup if desired. - *constraintOid = InvalidOid; - return NULL; -} diff --git a/third_party/spanner_pg/shims/catalog_shim_rewriteHandler.c b/third_party/spanner_pg/shims/catalog_shim_rewriteHandler.c deleted file mode 100644 index f2e673b1..00000000 --- a/third_party/spanner_pg/shims/catalog_shim_rewriteHandler.c +++ /dev/null @@ -1,72 +0,0 @@ -// -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -// Portions derived from source copyright Postgres Global Development Group in -// accordance with the following notice: -//------------------------------------------------------------------------------ -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2022 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -#include "third_party/spanner_pg/shims/catalog_shim.h" - -#include "third_party/spanner_pg/postgres_includes/all.h" - -void AcquireRewriteLocks(Query* parsetree, bool forExecute, - bool forUpdatePushedDown) { - // Do nothing here as it's a no-op. -} diff --git a/third_party/spanner_pg/shims/catalog_shim_test.cc b/third_party/spanner_pg/shims/catalog_shim_test.cc index e617e247..e213208e 100644 --- a/third_party/spanner_pg/shims/catalog_shim_test.cc +++ b/third_party/spanner_pg/shims/catalog_shim_test.cc @@ -1008,6 +1008,7 @@ TEST_F(CatalogShimTestWithMemory, FuncnameGetCandidatesNamespace) { /*nargs=*/-1)); EXPECT_TRUE(FunctionFound("spanner", "get_table_column_identity_state", /*nargs=*/1)); + EXPECT_TRUE(FunctionFound("spanner", "tokenize_jsonb", /*nargs=*/-1)); // Missing catalog for Spanner function. EXPECT_FALSE( @@ -1018,6 +1019,7 @@ TEST_F(CatalogShimTestWithMemory, FuncnameGetCandidatesNamespace) { FunctionFound(/*namespace_name=*/nullptr, "get_internal_sequence_state")); EXPECT_FALSE(FunctionFound(/*namespace_name=*/nullptr, "get_table_column_identity_state")); + EXPECT_FALSE(FunctionFound(/*namespace_name=*/nullptr, "tokenize_jsonb")); // Incorrect catalog for Spanner function. EXPECT_FALSE(FunctionFound("pg_catalog", "pending_commit_timestamp")); @@ -1025,7 +1027,7 @@ TEST_F(CatalogShimTestWithMemory, FuncnameGetCandidatesNamespace) { EXPECT_FALSE(FunctionFound("pg_catalog", "farm_fingerprint")); EXPECT_FALSE(FunctionFound("pg_catalog", "get_internal_sequence_state")); EXPECT_FALSE(FunctionFound("pg_catalog", "get_table_column_identity_state")); - + EXPECT_FALSE(FunctionFound("pg_catalog", "tokenize_jsonb")); } TEST_F(CatalogShimTestWithMemory, FuncGetDetailInt4Minus) { diff --git a/third_party/spanner_pg/shims/catalog_shim_timestamp.c b/third_party/spanner_pg/shims/catalog_shim_timestamp.c deleted file mode 100644 index a373a0b7..00000000 --- a/third_party/spanner_pg/shims/catalog_shim_timestamp.c +++ /dev/null @@ -1,131 +0,0 @@ -// -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -// Portions derived from source copyright Postgres Global Development Group in -// accordance with the following notice: -//------------------------------------------------------------------------------ -// PostgreSQL is released under the PostgreSQL License, a liberal Open Source -// license, similar to the BSD or MIT licenses. -// -// PostgreSQL Database Management System -// (formerly known as Postgres, then as Postgres95) -// -// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group -// -// Portions Copyright © 1994, The Regents of the University of California -// -// Portions Copyright 2023 Google LLC -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without a written agreement -// is hereby granted, provided that the above copyright notice and this -// paragraph and the following two paragraphs appear in all copies. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING -// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, -// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN -// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE -// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -//------------------------------------------------------------------------------ - -#include "third_party/spanner_pg/shims/catalog_shim.h" - -#include "third_party/spanner_pg/postgres_includes/all.h" - -/* timestamptz_in() - * Convert a string to internal form. - */ -Datum -timestamptz_in(PG_FUNCTION_ARGS) -{ - char *str = PG_GETARG_CSTRING(0); - -#ifdef NOT_USED - Oid typelem = PG_GETARG_OID(1); -#endif - int32_t typmod = PG_GETARG_INT32(2); - TimestampTz result; - fsec_t fsec; - struct pg_tm tt, - *tm = &tt; - int tz; - int dtype; - int nf; - int dterr; - char *field[MAXDATEFIELDS]; - int ftype[MAXDATEFIELDS]; - char workbuf[MAXDATELEN + MAXDATEFIELDS]; - - dterr = ParseDateTime(str, workbuf, sizeof(workbuf), - field, ftype, MAXDATEFIELDS, &nf); - if (dterr == 0) - dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz); - if (dterr != 0) - DateTimeParseError(dterr, str, "timestamp with time zone"); - - switch (dtype) - { - case DTK_DATE: - if (tm2timestamp(tm, fsec, &tz, &result) != 0) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("timestamp out of range: \"%s\"", str))); - break; - - case DTK_EPOCH: - /* SPANGRES: remove support for epoch */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("epoch is not supported"))); - - case DTK_LATE: - case DTK_EARLY: - /* SPANGRES: remove support for infinity and -infinity */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("infinity and -infinity are not supported"))); - - default: - elog(ERROR, "unexpected dtype %d while parsing timestamptz \"%s\"", - dtype, str); - TIMESTAMP_NOEND(result); - } - - AdjustTimestampForTypmod(&result, typmod); - - PG_RETURN_TIMESTAMPTZ(result); -} diff --git a/third_party/spanner_pg/shims/fake_catalog_shim.c b/third_party/spanner_pg/shims/fake_catalog_shim.c index cf459aef..08ab611c 100644 --- a/third_party/spanner_pg/shims/fake_catalog_shim.c +++ b/third_party/spanner_pg/shims/fake_catalog_shim.c @@ -84,10 +84,6 @@ bool can_coerce_type(int nargs, const Oid* input_typeids, abort(); } -Oid get_role_oid(const char *rolname, bool missing_ok) { - abort(); -} - char* format_type_extended(Oid type_oid, int32_t typemod, bits16 flags) { abort(); @@ -97,10 +93,6 @@ bool TypeIsVisible(Oid typid) { abort(); } -void ReleaseCatCache(HeapTuple tuple) { - abort(); -} - Type typeidType(Oid id) { abort(); } @@ -141,11 +133,6 @@ bool NamedCallMatchFound(Oid proc_oid, int nargs, List *argnames, abort(); } -void fmgr_info_cxt_security(Oid functionId, FmgrInfo* finfo, - MemoryContext mcxt, bool ignore_security) { - abort(); -} - void get_type_category_preferred(Oid typid, char* typcategory, bool* typispreferred) { abort(); @@ -162,11 +149,6 @@ void get_type_io_data(Oid typid, abort(); } -void AcquireRewriteLocks(Query* parsetree, bool forExecute, - bool forUpdatePushedDown) { - abort(); -} - FuncCandidateList OpernameGetCandidates(List* names, char oprkind, bool missing_schema_ok) { abort(); @@ -376,18 +358,6 @@ RegProcedure get_opcode(Oid opno) { abort(); } -LockAcquireResult LockAcquireExtended(const LOCKTAG* locktag, - LOCKMODE lockmode, - bool sessionLock, bool dontWait, - bool reportMemoryError, - LOCALLOCK** locallockp) { - abort(); -} - -void InitializeGUCOptions(void) { - abort(); -} - Oid get_range_subtype(Oid rangeOid) { abort(); } @@ -404,11 +374,6 @@ Const* make_const(ParseState* pstate, A_Const* aconst) { abort(); } -Bitmapset* get_primary_key_attnos(Oid relid, bool deferrableOk, - Oid* constraintOid) { - abort(); -} - Oid RelnameGetRelid(const char* relname) { abort(); } @@ -425,10 +390,6 @@ Oid get_namespace_oid(const char* nspname, bool missing_ok) { abort(); } -Oid get_database_oid(const char* dbname, bool missing_ok) { - abort(); -} - Oid get_commutator(Oid opno) { abort(); } @@ -517,14 +478,6 @@ Oid get_collation_oid(List *name, bool missing_ok) { abort(); } -Datum date_in(PG_FUNCTION_ARGS) { - abort(); -} - -Datum timestamptz_in(PG_FUNCTION_ARGS) { - abort(); -} - RegProcedure get_typsubscript(Oid typid, Oid *typelemp) { abort(); } @@ -533,18 +486,6 @@ Oid get_multirange_range(Oid multirangeOid) { abort(); } -int -DecodeDateTime(char **field, int *ftype, int nf, - int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp) { - abort(); -} - -int -DecodeTimezoneAbbrev(int field, char *lowtoken, - int *offset, pg_tz **tz) { - abort(); -} - Query *transformCallStmt(ParseState *pstate, CallStmt *stmt) { abort(); } diff --git a/third_party/spanner_pg/src/backend/catalog/pg_constraint.c b/third_party/spanner_pg/src/backend/catalog/pg_constraint.c index f663ba3b..bcc4df15 100644 --- a/third_party/spanner_pg/src/backend/catalog/pg_constraint.c +++ b/third_party/spanner_pg/src/backend/catalog/pg_constraint.c @@ -1108,82 +1108,14 @@ get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok) * on failure. */ Bitmapset * -get_primary_key_attnos_UNUSED_SPANGRES(Oid relid, bool deferrableOk, Oid *constraintOid) +get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid) { - Bitmapset *pkattnos = NULL; - Relation pg_constraint; - HeapTuple tuple; - SysScanDesc scan; - ScanKeyData skey[1]; - - /* Set *constraintOid, to avoid complaints about uninitialized vars */ - *constraintOid = InvalidOid; - - /* Scan pg_constraint for constraints of the target rel */ - pg_constraint = table_open(ConstraintRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], - Anum_pg_constraint_conrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - - scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true, - NULL, 1, skey); - - while (HeapTupleIsValid(tuple = systable_getnext(scan))) - { - Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); - Datum adatum; - bool isNull; - ArrayType *arr; - int16 *attnums; - int numkeys; - int i; - - /* Skip constraints that are not PRIMARY KEYs */ - if (con->contype != CONSTRAINT_PRIMARY) - continue; - - /* - * If the primary key is deferrable, but we've been instructed to - * ignore deferrable constraints, then we might as well give up - * searching, since there can only be a single primary key on a table. - */ - if (con->condeferrable && !deferrableOk) - break; - - /* Extract the conkey array, ie, attnums of PK's columns */ - adatum = heap_getattr(tuple, Anum_pg_constraint_conkey, - RelationGetDescr(pg_constraint), &isNull); - if (isNull) - elog(ERROR, "null conkey for constraint %u", - ((Form_pg_constraint) GETSTRUCT(tuple))->oid); - arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ - numkeys = ARR_DIMS(arr)[0]; - if (ARR_NDIM(arr) != 1 || - numkeys < 0 || - ARR_HASNULL(arr) || - ARR_ELEMTYPE(arr) != INT2OID) - elog(ERROR, "conkey is not a 1-D smallint array"); - attnums = (int16 *) ARR_DATA_PTR(arr); - - /* Construct the result value */ - for (i = 0; i < numkeys; i++) - { - pkattnos = bms_add_member(pkattnos, - attnums[i] - FirstLowInvalidHeapAttributeNumber); - } - *constraintOid = ((Form_pg_constraint) GETSTRUCT(tuple))->oid; - - /* No need to search further */ - break; - } - - systable_endscan(scan); - - table_close(pg_constraint, AccessShareLock); - - return pkattnos; + // SPANGRES BEGIN + // SPANGRES: this feature is unsupported. Return null set. + // TODO: Add support for primary key lookup if desired. + *constraintOid = InvalidOid; + return NULL; + // SPANGRES END } /* diff --git a/third_party/spanner_pg/src/backend/commands/dbcommands.c b/third_party/spanner_pg/src/backend/commands/dbcommands.c index 618c2826..3b4d657b 100644 --- a/third_party/spanner_pg/src/backend/commands/dbcommands.c +++ b/third_party/spanner_pg/src/backend/commands/dbcommands.c @@ -2979,41 +2979,20 @@ errdetail_busy_db(int notherbackends, int npreparedxacts) * true, just return InvalidOid. */ Oid -get_database_oid_UNUSED_SPANGRES(const char *dbname, bool missing_ok) +get_database_oid(const char *dbname, bool missing_ok) { - Relation pg_database; - ScanKeyData entry[1]; - SysScanDesc scan; - HeapTuple dbtuple; - Oid oid; - /* - * There's no syscache for pg_database indexed by name, so we must look - * the hard way. - */ - pg_database = table_open(DatabaseRelationId, AccessShareLock); - ScanKeyInit(&entry[0], - Anum_pg_database_datname, - BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(dbname)); - scan = systable_beginscan(pg_database, DatabaseNameIndexId, true, - NULL, 1, entry); - dbtuple = systable_getnext(scan); - /* We assume that there can be at most one matching tuple */ - if (HeapTupleIsValid(dbtuple)) - oid = ((Form_pg_database) GETSTRUCT(dbtuple))->oid; - else - oid = InvalidOid; - - systable_endscan(scan); - table_close(pg_database, AccessShareLock); - - if (!OidIsValid(oid) && !missing_ok) + // SPANGRES BEGIN + // TODO: Add support for namespace Oids. + if (missing_ok) { + return InvalidOid; + } else { ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%s\" does not exist", + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Tried to lookup Oid for database %s, but " + "using databases as queryable namespaces is not supported", dbname))); - - return oid; + } + // SPANGRES END } /* diff --git a/third_party/spanner_pg/src/backend/rewrite/rewriteHandler.c b/third_party/spanner_pg/src/backend/rewrite/rewriteHandler.c index 6e473dc7..cc0fcb3b 100644 --- a/third_party/spanner_pg/src/backend/rewrite/rewriteHandler.c +++ b/third_party/spanner_pg/src/backend/rewrite/rewriteHandler.c @@ -140,162 +140,13 @@ static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist); * construction of a nested join was O(N^2) in the nesting depth.) */ void -AcquireRewriteLocks_UNUSED_SPANGRES(Query *parsetree, +AcquireRewriteLocks(Query *parsetree, bool forExecute, bool forUpdatePushedDown) { - ListCell *l; - int rt_index; - acquireLocksOnSubLinks_context context; - - context.for_execute = forExecute; - - /* - * First, process RTEs of the current query level. - */ - rt_index = 0; - foreach(l, parsetree->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - Relation rel; - LOCKMODE lockmode; - List *newaliasvars; - Index curinputvarno; - RangeTblEntry *curinputrte; - ListCell *ll; - - ++rt_index; - switch (rte->rtekind) - { - case RTE_RELATION: - - /* - * Grab the appropriate lock type for the relation, and do not - * release it until end of transaction. This protects the - * rewriter, planner, and executor against schema changes - * mid-query. - * - * If forExecute is false, ignore rellockmode and just use - * AccessShareLock. - */ - if (!forExecute) - lockmode = AccessShareLock; - else if (forUpdatePushedDown) - { - /* Upgrade RTE's lock mode to reflect pushed-down lock */ - if (rte->rellockmode == AccessShareLock) - rte->rellockmode = RowShareLock; - lockmode = rte->rellockmode; - } - else - lockmode = rte->rellockmode; - - rel = table_open(rte->relid, lockmode); - - /* - * While we have the relation open, update the RTE's relkind, - * just in case it changed since this rule was made. - */ - rte->relkind = rel->rd_rel->relkind; - - table_close(rel, NoLock); - break; - - case RTE_JOIN: - - /* - * Scan the join's alias var list to see if any columns have - * been dropped, and if so replace those Vars with null - * pointers. - * - * Since a join has only two inputs, we can expect to see - * multiple references to the same input RTE; optimize away - * multiple fetches. - */ - newaliasvars = NIL; - curinputvarno = 0; - curinputrte = NULL; - foreach(ll, rte->joinaliasvars) - { - Var *aliasitem = (Var *) lfirst(ll); - Var *aliasvar = aliasitem; - - /* Look through any implicit coercion */ - aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar); - - /* - * If the list item isn't a simple Var, then it must - * represent a merged column, ie a USING column, and so it - * couldn't possibly be dropped, since it's referenced in - * the join clause. (Conceivably it could also be a null - * pointer already? But that's OK too.) - */ - if (aliasvar && IsA(aliasvar, Var)) - { - /* - * The elements of an alias list have to refer to - * earlier RTEs of the same rtable, because that's the - * order the planner builds things in. So we already - * processed the referenced RTE, and so it's safe to - * use get_rte_attribute_is_dropped on it. (This might - * not hold after rewriting or planning, but it's OK - * to assume here.) - */ - Assert(aliasvar->varlevelsup == 0); - if (aliasvar->varno != curinputvarno) - { - curinputvarno = aliasvar->varno; - if (curinputvarno >= rt_index) - elog(ERROR, "unexpected varno %d in JOIN RTE %d", - curinputvarno, rt_index); - curinputrte = rt_fetch(curinputvarno, - parsetree->rtable); - } - if (get_rte_attribute_is_dropped(curinputrte, - aliasvar->varattno)) - { - /* Replace the join alias item with a NULL */ - aliasitem = NULL; - } - } - newaliasvars = lappend(newaliasvars, aliasitem); - } - rte->joinaliasvars = newaliasvars; - break; - - case RTE_SUBQUERY: - - /* - * The subquery RTE itself is all right, but we have to - * recurse to process the represented subquery. - */ - AcquireRewriteLocks(rte->subquery, - forExecute, - (forUpdatePushedDown || - get_parse_rowmark(parsetree, rt_index) != NULL)); - break; - - default: - /* ignore other types of RTEs */ - break; - } - } - - /* Recurse into subqueries in WITH */ - foreach(l, parsetree->cteList) - { - CommonTableExpr *cte = (CommonTableExpr *) lfirst(l); - - AcquireRewriteLocks((Query *) cte->ctequery, forExecute, false); - } - - /* - * Recurse into sublink subqueries, too. But we already did the ones in - * the rtable and cteList. - */ - if (parsetree->hasSubLinks) - query_tree_walker(parsetree, acquireLocksOnSubLinks, &context, - QTW_IGNORE_RC_SUBQUERIES); + // SPANGRES BEGIN + // Do nothing here as it's a no-op. + // SPANGRES END } /* diff --git a/third_party/spanner_pg/src/backend/storage/lmgr/lock.c b/third_party/spanner_pg/src/backend/storage/lmgr/lock.c index e2e0d1e1..deb1e0d2 100644 --- a/third_party/spanner_pg/src/backend/storage/lmgr/lock.c +++ b/third_party/spanner_pg/src/backend/storage/lmgr/lock.c @@ -758,385 +758,19 @@ LockAcquire(const LOCKTAG *locktag, * table entry if a lock is successfully acquired, or NULL if not. */ LockAcquireResult -LockAcquireExtended_UNUSED_SPANGRES(const LOCKTAG *locktag, +LockAcquireExtended(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock, bool dontWait, bool reportMemoryError, LOCALLOCK **locallockp) { - LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid; - LockMethod lockMethodTable; - LOCALLOCKTAG localtag; - LOCALLOCK *locallock; - LOCK *lock; - PROCLOCK *proclock; - bool found; - ResourceOwner owner; - uint32 hashcode; - LWLock *partitionLock; - bool found_conflict; - bool log_lock = false; - - if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods)) - elog(ERROR, "unrecognized lock method: %d", lockmethodid); - lockMethodTable = LockMethods[lockmethodid]; - if (lockmode <= 0 || lockmode > lockMethodTable->numLockModes) - elog(ERROR, "unrecognized lock mode: %d", lockmode); - - if (RecoveryInProgress() && !InRecovery && - (locktag->locktag_type == LOCKTAG_OBJECT || - locktag->locktag_type == LOCKTAG_RELATION) && - lockmode > RowExclusiveLock) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("cannot acquire lock mode %s on database objects while recovery is in progress", - lockMethodTable->lockModeNames[lockmode]), - errhint("Only RowExclusiveLock or less can be acquired on database objects during recovery."))); - -#ifdef LOCK_DEBUG - if (LOCK_DEBUG_ENABLED(locktag)) - elog(LOG, "LockAcquire: lock [%u,%u] %s", - locktag->locktag_field1, locktag->locktag_field2, - lockMethodTable->lockModeNames[lockmode]); -#endif - - /* Identify owner for lock */ - if (sessionLock) - owner = NULL; - else - owner = CurrentResourceOwner; - - /* - * Find or create a LOCALLOCK entry for this lock and lockmode - */ - MemSet(&localtag, 0, sizeof(localtag)); /* must clear padding */ - localtag.lock = *locktag; - localtag.mode = lockmode; - - locallock = (LOCALLOCK *) hash_search(LockMethodLocalHash, - (void *) &localtag, - HASH_ENTER, &found); - - /* - * if it's a new locallock object, initialize it - */ - if (!found) - { - locallock->lock = NULL; - locallock->proclock = NULL; - locallock->hashcode = LockTagHashCode(&(localtag.lock)); - locallock->nLocks = 0; - locallock->holdsStrongLockCount = false; - locallock->lockCleared = false; - locallock->numLockOwners = 0; - locallock->maxLockOwners = 8; - locallock->lockOwners = NULL; /* in case next line fails */ - locallock->lockOwners = (LOCALLOCKOWNER *) - MemoryContextAlloc(TopMemoryContext, - locallock->maxLockOwners * sizeof(LOCALLOCKOWNER)); - } - else - { - /* Make sure there will be room to remember the lock */ - if (locallock->numLockOwners >= locallock->maxLockOwners) - { - int newsize = locallock->maxLockOwners * 2; - - locallock->lockOwners = (LOCALLOCKOWNER *) - repalloc(locallock->lockOwners, - newsize * sizeof(LOCALLOCKOWNER)); - locallock->maxLockOwners = newsize; - } - } - hashcode = locallock->hashcode; - - if (locallockp) - *locallockp = locallock; - - /* - * If we already hold the lock, we can just increase the count locally. - * - * If lockCleared is already set, caller need not worry about absorbing - * sinval messages related to the lock's object. - */ - if (locallock->nLocks > 0) - { - GrantLockLocal(locallock, owner); - if (locallock->lockCleared) - return LOCKACQUIRE_ALREADY_CLEAR; - else - return LOCKACQUIRE_ALREADY_HELD; - } - - /* - * We don't acquire any other heavyweight lock while holding the relation - * extension lock. We do allow to acquire the same relation extension - * lock more than once but that case won't reach here. - */ - Assert(!IsRelationExtensionLockHeld); - - /* - * Prepare to emit a WAL record if acquisition of this lock needs to be - * replayed in a standby server. - * - * Here we prepare to log; after lock is acquired we'll issue log record. - * This arrangement simplifies error recovery in case the preparation step - * fails. - * - * Only AccessExclusiveLocks can conflict with lock types that read-only - * transactions can acquire in a standby server. Make sure this definition - * matches the one in GetRunningTransactionLocks(). - */ - if (lockmode >= AccessExclusiveLock && - locktag->locktag_type == LOCKTAG_RELATION && - !RecoveryInProgress() && - XLogStandbyInfoActive()) - { - LogAccessExclusiveLockPrepare(); - log_lock = true; - } - - /* - * Attempt to take lock via fast path, if eligible. But if we remember - * having filled up the fast path array, we don't attempt to make any - * further use of it until we release some locks. It's possible that some - * other backend has transferred some of those locks to the shared hash - * table, leaving space free, but it's not worth acquiring the LWLock just - * to check. It's also possible that we're acquiring a second or third - * lock type on a relation we have already locked using the fast-path, but - * for now we don't worry about that case either. - */ - if (EligibleForRelationFastPath(locktag, lockmode) && - FastPathLocalUseCount < FP_LOCK_SLOTS_PER_BACKEND) - { - uint32 fasthashcode = FastPathStrongLockHashPartition(hashcode); - bool acquired; - - /* - * LWLockAcquire acts as a memory sequencing point, so it's safe to - * assume that any strong locker whose increment to - * FastPathStrongRelationLocks->counts becomes visible after we test - * it has yet to begin to transfer fast-path locks. - */ - LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE); - if (FastPathStrongRelationLocks->count[fasthashcode] != 0) - acquired = false; - else - acquired = FastPathGrantRelationLock(locktag->locktag_field2, - lockmode); - LWLockRelease(&MyProc->fpInfoLock); - if (acquired) - { - /* - * The locallock might contain stale pointers to some old shared - * objects; we MUST reset these to null before considering the - * lock to be acquired via fast-path. - */ - locallock->lock = NULL; - locallock->proclock = NULL; - GrantLockLocal(locallock, owner); - return LOCKACQUIRE_OK; - } - } - - /* - * If this lock could potentially have been taken via the fast-path by - * some other backend, we must (temporarily) disable further use of the - * fast-path for this lock tag, and migrate any locks already taken via - * this method to the main lock table. - */ - if (ConflictsWithRelationFastPath(locktag, lockmode)) - { - uint32 fasthashcode = FastPathStrongLockHashPartition(hashcode); - - BeginStrongLockAcquire(locallock, fasthashcode); - if (!FastPathTransferRelationLocks(lockMethodTable, locktag, - hashcode)) - { - AbortStrongLockAcquire(); - if (locallock->nLocks == 0) - RemoveLocalLock(locallock); - if (locallockp) - *locallockp = NULL; - if (reportMemoryError) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of shared memory"), - errhint("You might need to increase max_locks_per_transaction."))); - else - return LOCKACQUIRE_NOT_AVAIL; - } - } - - /* - * We didn't find the lock in our LOCALLOCK table, and we didn't manage to - * take it via the fast-path, either, so we've got to mess with the shared - * lock table. - */ - partitionLock = LockHashPartitionLock(hashcode); - - LWLockAcquire(partitionLock, LW_EXCLUSIVE); - - /* - * Find or create lock and proclock entries with this tag - * - * Note: if the locallock object already existed, it might have a pointer - * to the lock already ... but we should not assume that that pointer is - * valid, since a lock object with zero hold and request counts can go - * away anytime. So we have to use SetupLockInTable() to recompute the - * lock and proclock pointers, even if they're already set. - */ - proclock = SetupLockInTable(lockMethodTable, MyProc, locktag, - hashcode, lockmode); - if (!proclock) - { - AbortStrongLockAcquire(); - LWLockRelease(partitionLock); - if (locallock->nLocks == 0) - RemoveLocalLock(locallock); - if (locallockp) - *locallockp = NULL; - if (reportMemoryError) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of shared memory"), - errhint("You might need to increase max_locks_per_transaction."))); - else - return LOCKACQUIRE_NOT_AVAIL; - } - locallock->proclock = proclock; - lock = proclock->tag.myLock; - locallock->lock = lock; - - /* - * If lock requested conflicts with locks requested by waiters, must join - * wait queue. Otherwise, check for conflict with already-held locks. - * (That's last because most complex check.) - */ - if (lockMethodTable->conflictTab[lockmode] & lock->waitMask) - found_conflict = true; - else - found_conflict = LockCheckConflicts(lockMethodTable, lockmode, - lock, proclock); - - if (!found_conflict) - { - /* No conflict with held or previously requested locks */ - GrantLock(lock, proclock, lockmode); - GrantLockLocal(locallock, owner); - } - else - { - /* - * We can't acquire the lock immediately. If caller specified no - * blocking, remove useless table entries and return - * LOCKACQUIRE_NOT_AVAIL without waiting. - */ - if (dontWait) - { - AbortStrongLockAcquire(); - if (proclock->holdMask == 0) - { - uint32 proclock_hashcode; - - proclock_hashcode = ProcLockHashCode(&proclock->tag, hashcode); - SHMQueueDelete(&proclock->lockLink); - SHMQueueDelete(&proclock->procLink); - if (!hash_search_with_hash_value(LockMethodProcLockHash, - (void *) &(proclock->tag), - proclock_hashcode, - HASH_REMOVE, - NULL)) - elog(PANIC, "proclock table corrupted"); - } - else - PROCLOCK_PRINT("LockAcquire: NOWAIT", proclock); - lock->nRequested--; - lock->requested[lockmode]--; - LOCK_PRINT("LockAcquire: conditional lock failed", lock, lockmode); - Assert((lock->nRequested > 0) && (lock->requested[lockmode] >= 0)); - Assert(lock->nGranted <= lock->nRequested); - LWLockRelease(partitionLock); - if (locallock->nLocks == 0) - RemoveLocalLock(locallock); - if (locallockp) - *locallockp = NULL; - return LOCKACQUIRE_NOT_AVAIL; - } - - /* - * Set bitmask of locks this process already holds on this object. - */ - MyProc->heldLocks = proclock->holdMask; - - /* - * Sleep till someone wakes me up. - */ - - TRACE_POSTGRESQL_LOCK_WAIT_START(locktag->locktag_field1, - locktag->locktag_field2, - locktag->locktag_field3, - locktag->locktag_field4, - locktag->locktag_type, - lockmode); - - WaitOnLock(locallock, owner); - - TRACE_POSTGRESQL_LOCK_WAIT_DONE(locktag->locktag_field1, - locktag->locktag_field2, - locktag->locktag_field3, - locktag->locktag_field4, - locktag->locktag_type, - lockmode); - - /* - * NOTE: do not do any material change of state between here and - * return. All required changes in locktable state must have been - * done when the lock was granted to us --- see notes in WaitOnLock. - */ - - /* - * Check the proclock entry status, in case something in the ipc - * communication doesn't work correctly. - */ - if (!(proclock->holdMask & LOCKBIT_ON(lockmode))) - { - AbortStrongLockAcquire(); - PROCLOCK_PRINT("LockAcquire: INCONSISTENT", proclock); - LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode); - /* Should we retry ? */ - LWLockRelease(partitionLock); - elog(ERROR, "LockAcquire failed"); - } - PROCLOCK_PRINT("LockAcquire: granted", proclock); - LOCK_PRINT("LockAcquire: granted", lock, lockmode); - } - - /* - * Lock state is fully up-to-date now; if we error out after this, no - * special error cleanup is required. - */ - FinishStrongLockAcquire(); - - LWLockRelease(partitionLock); - - /* - * Emit a WAL record if acquisition of this lock needs to be replayed in a - * standby server. - */ - if (log_lock) - { - /* - * Decode the locktag back to the original values, to avoid sending - * lots of empty bytes with every message. See lock.h to check how a - * locktag is defined for LOCKTAG_RELATION - */ - LogAccessExclusiveLock(locktag->locktag_field1, - locktag->locktag_field2); - } - - return LOCKACQUIRE_OK; + // SPANGRES BEGIN + // Codepath should never be supported in Spangres, so we throw a safe error. + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Attempted an unsupported operation in the PostgreSQL " + "locking subsystem"))); + // SPANGRES END } /* diff --git a/third_party/spanner_pg/src/backend/utils/adt/acl.c b/third_party/spanner_pg/src/backend/utils/adt/acl.c index 6cb8b281..00458b75 100644 --- a/third_party/spanner_pg/src/backend/utils/adt/acl.c +++ b/third_party/spanner_pg/src/backend/utils/adt/acl.c @@ -5169,25 +5169,27 @@ select_best_grantor(Oid roleId, AclMode privileges, } } +// SPANGRES BEGIN /* * get_role_oid - Given a role name, look up the role's OID. * * If missing_ok is false, throw an error if role name not found. If * true, just return InvalidOid. + * + * SPANGRES: Currently, we do not support roles so we always return either + * InvalidOid or throw an error. This will need to be updated when we add + * support for roles. */ Oid -get_role_oid_UNUSED_SPANGRES(const char *rolname, bool missing_ok) +get_role_oid(const char *rolname, bool missing_ok) { - Oid oid; - - oid = GetSysCacheOid1(AUTHNAME, Anum_pg_authid_oid, - CStringGetDatum(rolname)); - if (!OidIsValid(oid) && !missing_ok) + if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("role \"%s\" does not exist", rolname))); - return oid; + return InvalidOid; } +// SPANGRES END /* * get_role_oid_or_public - As above, but return ACL_ID_PUBLIC if the diff --git a/third_party/spanner_pg/src/backend/utils/adt/date.c b/third_party/spanner_pg/src/backend/utils/adt/date.c index 3338bca5..12c252d5 100644 --- a/third_party/spanner_pg/src/backend/utils/adt/date.c +++ b/third_party/spanner_pg/src/backend/utils/adt/date.c @@ -109,7 +109,7 @@ anytime_typmodout(bool istz, int32 typmod) * Given date text string, convert to internal date format. */ Datum -date_in_UNUSED_SPANGRES(PG_FUNCTION_ARGS) +date_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); DateADT date; @@ -136,17 +136,20 @@ date_in_UNUSED_SPANGRES(PG_FUNCTION_ARGS) case DTK_DATE: break; + // SPANGRES BEGIN case DTK_EPOCH: - GetEpochTime(tm); - break; + /* SPANGRES: remove support for epoch */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("epoch is not supported"))); case DTK_LATE: - DATE_NOEND(date); - PG_RETURN_DATEADT(date); - case DTK_EARLY: - DATE_NOBEGIN(date); - PG_RETURN_DATEADT(date); + /* SPANGRES: remove support for infinity and -infinity */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("infinity and -infinity are not supported"))); + // SPANGRES END default: DateTimeParseError(DTERR_BAD_FORMAT, str, "date"); diff --git a/third_party/spanner_pg/src/backend/utils/adt/datetime.c b/third_party/spanner_pg/src/backend/utils/adt/datetime.c index 580d870c..1b8fa08f 100644 --- a/third_party/spanner_pg/src/backend/utils/adt/datetime.c +++ b/third_party/spanner_pg/src/backend/utils/adt/datetime.c @@ -1003,7 +1003,7 @@ ParseDateTime(const char *timestr, char *workbuf, size_t buflen, * 1997-05-27 */ int -DecodeDateTime_UNUSED_SPANGRES(char **field, int *ftype, int nf, +DecodeDateTime(char **field, int *ftype, int nf, int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp) { int fmask = 0, @@ -1022,7 +1022,10 @@ DecodeDateTime_UNUSED_SPANGRES(char **field, int *ftype, int nf, pg_tz *abbrevTz = NULL; pg_tz *valtz; char *abbrev = NULL; - struct pg_tm cur_tm; + // SPANGRES BEGIN + // Removes unused variable + // struct pg_tm cur_tm; + // SPANGRES END /* * We'll insist on at least all of the date fields, but initialize the @@ -1312,9 +1315,13 @@ DecodeDateTime_UNUSED_SPANGRES(char **field, int *ftype, int nf, { double time; - dterr = ParseFraction(cp, &time); - if (dterr) - return dterr; + // SPANGRES BEGIN + // Fixes *SAN violation + errno = 0; + time = strtod(cp, &cp); + if (*cp != '\0' || errno != 0) + return DTERR_BAD_FORMAT; + // SPANGRES END time *= USECS_PER_DAY; dt2time(time, &tm->tm_hour, &tm->tm_min, @@ -1414,42 +1421,32 @@ DecodeDateTime_UNUSED_SPANGRES(char **field, int *ftype, int nf, if (type == IGNORE_DTF) continue; - tmask = DTK_M(type); + // SPANGRES BEGIN + // SPANGRES: do not call DTK_M(UNKNOWN_FIELD), which is 1 << 31 + // and undefined behavior. For b/300504021. + // If the type is UNKNOWN_FIELD, the code below will either + // return an error or set tmask to DTK_M(TZ). It does not rely + // on tmask being set here. + if (type != UNKNOWN_FIELD) + { + tmask = DTK_M(type); + } + // SPANGRES END switch (type) { case RESERV: switch (val) { + // SPANGRES BEGIN case DTK_NOW: - tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ)); - *dtype = DTK_DATE; - GetCurrentTimeUsec(tm, fsec, tzp); - break; - case DTK_YESTERDAY: - tmask = DTK_DATE_M; - *dtype = DTK_DATE; - GetCurrentDateTime(&cur_tm); - j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) - 1, - &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - break; - case DTK_TODAY: - tmask = DTK_DATE_M; - *dtype = DTK_DATE; - GetCurrentDateTime(&cur_tm); - tm->tm_year = cur_tm.tm_year; - tm->tm_mon = cur_tm.tm_mon; - tm->tm_mday = cur_tm.tm_mday; - break; - case DTK_TOMORROW: - tmask = DTK_DATE_M; - *dtype = DTK_DATE; - GetCurrentDateTime(&cur_tm); - j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) + 1, - &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - break; + /* SPANGRES: return an error for relative timestamps */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("current/relative datetimes are not supported"))); + // SPANGRES END case DTK_ZULU: tmask = (DTK_TIME_M | DTK_M(TZ)); @@ -3273,22 +3270,24 @@ DecodeTimezone(char *str, int *tzp) * will be related in format. */ int -DecodeTimezoneAbbrev_UNUSED_SPANGRES(int field, char *lowtoken, +DecodeTimezoneAbbrev(int field, char *lowtoken, int *offset, pg_tz **tz) { int type; const datetkn *tp; tp = abbrevcache[field]; + // SPANGRES BEGIN /* use strncmp so that we match truncated tokens */ + /* + * SPANGRES: use the hardcoded SpangresTimezoneAbbrevTable instead of a zoneabbrevtbl + * loaded from a file + */ if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0) { - if (zoneabbrevtbl) - tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs, - zoneabbrevtbl->numabbrevs); - else - tp = NULL; + tp = datebsearch(lowtoken, SpangresTimezoneAbbrevTable, SpangresTimezoneAbbrevTableSize); } + // SPANGRES END if (tp == NULL) { type = UNKNOWN_FIELD; @@ -3299,16 +3298,12 @@ DecodeTimezoneAbbrev_UNUSED_SPANGRES(int field, char *lowtoken, { abbrevcache[field] = tp; type = tp->type; - if (type == DYNTZ) - { - *offset = 0; - *tz = FetchDynamicTimeZone(zoneabbrevtbl, tp); - } - else - { - *offset = tp->value; - *tz = NULL; - } + // SPANGRES BEGIN + /* SPANGRES: dynamic timezone abbreviations are not supported*/ + Assert(type != DYNTZ); + *offset = tp->value; + *tz = NULL; + // SPANGRES END } return type; diff --git a/third_party/spanner_pg/src/backend/utils/adt/timestamp.c b/third_party/spanner_pg/src/backend/utils/adt/timestamp.c index 160aec1a..dff8cc3b 100644 --- a/third_party/spanner_pg/src/backend/utils/adt/timestamp.c +++ b/third_party/spanner_pg/src/backend/utils/adt/timestamp.c @@ -407,7 +407,7 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod) * Convert a string to internal form. */ Datum -timestamptz_in_UNUSED_SPANGRES(PG_FUNCTION_ARGS) +timestamptz_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); @@ -443,17 +443,20 @@ timestamptz_in_UNUSED_SPANGRES(PG_FUNCTION_ARGS) errmsg("timestamp out of range: \"%s\"", str))); break; + // SPANGRES BEGIN case DTK_EPOCH: - result = SetEpochTimestamp(); - break; + /* SPANGRES: remove support for epoch */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("epoch is not supported"))); case DTK_LATE: - TIMESTAMP_NOEND(result); - break; - case DTK_EARLY: - TIMESTAMP_NOBEGIN(result); - break; + /* SPANGRES: remove support for infinity and -infinity */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("infinity and -infinity are not supported"))); + // SPANGRES END default: elog(ERROR, "unexpected dtype %d while parsing timestamptz \"%s\"", diff --git a/third_party/spanner_pg/src/backend/utils/cache/catcache.c b/third_party/spanner_pg/src/backend/utils/cache/catcache.c index 987884b2..4fc98801 100644 --- a/third_party/spanner_pg/src/backend/utils/cache/catcache.c +++ b/third_party/spanner_pg/src/backend/utils/cache/catcache.c @@ -1459,25 +1459,11 @@ SearchCatCacheMiss(CatCache *cache, * to catch references to already-released catcache entries. */ void -ReleaseCatCache_UNUSED_SPANGRES(HeapTuple tuple) +ReleaseCatCache(HeapTuple tuple) { - CatCTup *ct = (CatCTup *) (((char *) tuple) - - offsetof(CatCTup, tuple)); - - /* Safety checks to ensure we were handed a cache entry */ - Assert(ct->ct_magic == CT_MAGIC); - Assert(ct->refcount > 0); - - ct->refcount--; - ResourceOwnerForgetCatCacheRef(CurrentResourceOwner, &ct->tuple); - - if ( -#ifndef CATCACHE_FORCE_RELEASE - ct->dead && -#endif - ct->refcount == 0 && - (ct->c_list == NULL || ct->c_list->refcount == 0)) - CatCacheRemoveCTup(ct->my_cache, ct); + // SPANGRES BEGIN + // Do nothing + // SPANGRES END } diff --git a/third_party/spanner_pg/src/backend/utils/fmgr/fmgr.c b/third_party/spanner_pg/src/backend/utils/fmgr/fmgr.c index f3630a45..9e3e2b0a 100644 --- a/third_party/spanner_pg/src/backend/utils/fmgr/fmgr.c +++ b/third_party/spanner_pg/src/backend/utils/fmgr/fmgr.c @@ -55,7 +55,7 @@ typedef struct static HTAB *CFuncHash = NULL; -static void fmgr_info_cxt_security_UNUSED_SPANGRES(Oid functionId, FmgrInfo *finfo, +static void fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, bool ignore_security); static void fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple); static void fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple); @@ -142,20 +142,21 @@ fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt) fmgr_info_cxt_security(functionId, finfo, mcxt, false); } +// SPANGRES BEGIN +// Populates FmgrInfo with function call information (based on Oid lookup). +// Spangres version is exactly the same for built-in functions, but doesn't +// support non-built-in functions at all. The PostgreSQL version falls back to a +// lookup in the pg_proc table. Since we don't have a pg_proc table (just +// bootstrap catalog), this doesn't miss anything. /* * This one does the actual work. ignore_security is ordinarily false * but is set to true when we need to avoid recursion. */ static void -fmgr_info_cxt_security_UNUSED_SPANGRES(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, +fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, bool ignore_security) { - const FmgrBuiltin *fbp; - HeapTuple procedureTuple; - Form_pg_proc procedureStruct; - Datum prosrcdatum; - bool isnull; - char *prosrc; + const FmgrBuiltin* fbp; /* * fn_oid *must* be filled in last. Some code assumes that if fn_oid is @@ -165,108 +166,26 @@ fmgr_info_cxt_security_UNUSED_SPANGRES(Oid functionId, FmgrInfo *finfo, MemoryCo finfo->fn_oid = InvalidOid; finfo->fn_extra = NULL; finfo->fn_mcxt = mcxt; - finfo->fn_expr = NULL; /* caller may set this later */ + finfo->fn_expr = NULL; // caller may set this later - if ((fbp = fmgr_isbuiltin(functionId)) != NULL) - { + if ((fbp = fmgr_isbuiltin(functionId)) != NULL) { /* * Fast path for builtin functions: don't bother consulting pg_proc */ finfo->fn_nargs = fbp->nargs; finfo->fn_strict = fbp->strict; finfo->fn_retset = fbp->retset; - finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ + finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ finfo->fn_addr = fbp->func; finfo->fn_oid = functionId; return; } - /* Otherwise we need the pg_proc entry */ - procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); - if (!HeapTupleIsValid(procedureTuple)) - elog(ERROR, "cache lookup failed for function %u", functionId); - procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); - - finfo->fn_nargs = procedureStruct->pronargs; - finfo->fn_strict = procedureStruct->proisstrict; - finfo->fn_retset = procedureStruct->proretset; - - /* - * If it has prosecdef set, non-null proconfig, or if a plugin wants to - * hook function entry/exit, use fmgr_security_definer call handler --- - * unless we are being called again by fmgr_security_definer or - * fmgr_info_other_lang. - * - * When using fmgr_security_definer, function stats tracking is always - * disabled at the outer level, and instead we set the flag properly in - * fmgr_security_definer's private flinfo and implement the tracking - * inside fmgr_security_definer. This loses the ability to charge the - * overhead of fmgr_security_definer to the function, but gains the - * ability to set the track_functions GUC as a local GUC parameter of an - * interesting function and have the right things happen. - */ - if (!ignore_security && - (procedureStruct->prosecdef || - !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig, NULL) || - FmgrHookIsNeeded(functionId))) - { - finfo->fn_addr = fmgr_security_definer; - finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ - finfo->fn_oid = functionId; - ReleaseSysCache(procedureTuple); - return; - } - - switch (procedureStruct->prolang) - { - case INTERNALlanguageId: - - /* - * For an ordinary builtin function, we should never get here - * because the fmgr_isbuiltin() search above will have succeeded. - * However, if the user has done a CREATE FUNCTION to create an - * alias for a builtin function, we can end up here. In that case - * we have to look up the function by name. The name of the - * internal function is stored in prosrc (it doesn't have to be - * the same as the name of the alias!) - */ - prosrcdatum = SysCacheGetAttr(PROCOID, procedureTuple, - Anum_pg_proc_prosrc, &isnull); - if (isnull) - elog(ERROR, "null prosrc"); - prosrc = TextDatumGetCString(prosrcdatum); - fbp = fmgr_lookupByName(prosrc); - if (fbp == NULL) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), - errmsg("internal function \"%s\" is not in internal lookup table", - prosrc))); - pfree(prosrc); - /* Should we check that nargs, strict, retset match the table? */ - finfo->fn_addr = fbp->func; - /* note this policy is also assumed in fast path above */ - finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ - break; - - case ClanguageId: - fmgr_info_C_lang(functionId, finfo, procedureTuple); - finfo->fn_stats = TRACK_FUNC_PL; /* ie, track if ALL */ - break; - - case SQLlanguageId: - finfo->fn_addr = fmgr_sql; - finfo->fn_stats = TRACK_FUNC_PL; /* ie, track if ALL */ - break; - - default: - fmgr_info_other_lang(functionId, finfo, procedureTuple); - finfo->fn_stats = TRACK_FUNC_OFF; /* ie, track if not OFF */ - break; - } - - finfo->fn_oid = functionId; - ReleaseSysCache(procedureTuple); + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("Unknown function or function is not a built-in: %u", + functionId))); } +// SPANGRES END /* * Return module and C function name providing implementation of functionId. diff --git a/third_party/spanner_pg/src/backend/utils/misc/guc.c b/third_party/spanner_pg/src/backend/utils/misc/guc.c index ecd2ee6e..67e2305f 100644 --- a/third_party/spanner_pg/src/backend/utils/misc/guc.c +++ b/third_party/spanner_pg/src/backend/utils/misc/guc.c @@ -5838,50 +5838,18 @@ check_GUC_name_for_parameter_acl(const char *name) * processed command-line switches. */ void -InitializeGUCOptions_UNUSED_SPANGRES(void) -{ - int i; - - /* - * Before log_line_prefix could possibly receive a nonempty setting, make - * sure that timezone processing is minimally alive (see elog.c). - */ - pg_timezone_initialize(); - - /* - * Build sorted array of all GUC variables. - */ - build_guc_variables(); - - /* - * Load all variables with their compiled-in defaults, and initialize - * status fields as needed. - */ - for (i = 0; i < num_guc_variables; i++) - { - InitializeOneGUCOption(guc_variables[i]); - } - - guc_dirty = false; - - reporting_enabled = false; - - /* - * Prevent any attempt to override the transaction modes from - * non-interactive sources. - */ - SetConfigOption("transaction_isolation", "default", - PGC_POSTMASTER, PGC_S_OVERRIDE); - SetConfigOption("transaction_read_only", "no", - PGC_POSTMASTER, PGC_S_OVERRIDE); - SetConfigOption("transaction_deferrable", "no", - PGC_POSTMASTER, PGC_S_OVERRIDE); - - /* - * For historical reasons, some GUC parameters can receive defaults from - * environment variables. Process those settings. - */ - InitializeGUCOptionsFromEnvironment(); +InitializeGUCOptions(void) +{ + // SPANGRES BEGIN + // Codepath should never be supported in Spangres, so we throw a safe error. + // Guarantee that `guc_variables` is not built so that we can treat + // GUC-controlled variables as simple globals for evaluating their access + // patterns and thread safety. + ereport( + ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("PostgreSQL's configuration system (GUC) is not supported."))); + // SPANGRES END } /* diff --git a/third_party/spanner_pg/transformer/BUILD b/third_party/spanner_pg/transformer/BUILD index 2c9d3d2b..80f905db 100644 --- a/third_party/spanner_pg/transformer/BUILD +++ b/third_party/spanner_pg/transformer/BUILD @@ -184,6 +184,7 @@ cc_test( "//third_party/spanner_pg/src/backend:backend_with_shims", "//third_party/spanner_pg/test_catalog", "//third_party/spanner_pg/util:postgres", + "//third_party/spanner_pg/util:uuid_conversion", "//third_party/spanner_pg/util:valid_memory_context_fixture", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/flags:flag", diff --git a/third_party/spanner_pg/transformer/expr_test.cc b/third_party/spanner_pg/transformer/expr_test.cc index fbe1d3f7..645efb57 100644 --- a/third_party/spanner_pg/transformer/expr_test.cc +++ b/third_party/spanner_pg/transformer/expr_test.cc @@ -59,6 +59,7 @@ #include "third_party/spanner_pg/transformer/forward_transformer.h" #include "third_party/spanner_pg/transformer/transformer_test.h" #include "third_party/spanner_pg/util/postgres.h" +#include "third_party/spanner_pg/util/uuid_conversion.h" #include "third_party/spanner_pg/util/valid_memory_context_fixture.h" #include "zetasql/base/status_macros.h" diff --git a/third_party/spanner_pg/transformer/forward_query.cc b/third_party/spanner_pg/transformer/forward_query.cc index d8bb4ee4..2cc89138 100644 --- a/third_party/spanner_pg/transformer/forward_query.cc +++ b/third_party/spanner_pg/transformer/forward_query.cc @@ -1304,12 +1304,20 @@ ForwardTransformer::BuildGsqlResolvedArrayScan( internal::PostgresCastToNode(list_nth(rte.eref->colnames, 0)); ZETASQL_RET_CHECK(IsA(column_name, String)); std::string column_name_str(strVal(column_name)); + const zetasql::Type* element_type = + resolved_array_expr_argument->type()->AsArray()->element_type(); + const zetasql::AnnotationMap* element_annotation_map = + resolved_array_expr_argument->type_annotation_map() != nullptr && + resolved_array_expr_argument->type_annotation_map()->IsArrayMap() + ? resolved_array_expr_argument->type_annotation_map() + ->AsArrayMap() + ->element() + : resolved_array_expr_argument->type_annotation_map(); ZETASQL_ASSIGN_OR_RETURN( const zetasql::ResolvedColumn array_element_column, BuildNewGsqlResolvedColumn( rte.eref->aliasname, column_name_str, - resolved_array_expr_argument->type()->AsArray()->element_type(), - resolved_array_expr_argument->type_annotation_map())); + element_type, element_annotation_map)); // We register one output Var under the assumption that UNNEST always produces // exactly one output column from this RTE. This is true as long as we are // unnesting only a single array (checked above) and not supporting WITH diff --git a/third_party/spanner_pg/transformer/query_test.cc b/third_party/spanner_pg/transformer/query_test.cc index 38913094..ddb80e4c 100644 --- a/third_party/spanner_pg/transformer/query_test.cc +++ b/third_party/spanner_pg/transformer/query_test.cc @@ -155,6 +155,23 @@ void StripTargetEntry(TargetEntry* entry) { entry->resorigcol = InvalidOid; } +// The has_explicit_type boolean field on a ZetaSQL literal is not used during +// query execution and is not possible for the Spangres transformer to match +// exactly. This method strips the has_explicit_type field from a resolved AST +// debug string so that we can compare debug strings between Spangres and +// ZetaSQL. +std::string StripHasExplicitType( + const std::string& original_debug_string) { + const std::string kHasExplicitType = ", has_explicit_type=TRUE"; + std::vector lines = absl::StrSplit(original_debug_string, '\n'); + std::vector stripped_lines; + for (auto& line : lines) { + RE2::GlobalReplace(&line, kHasExplicitType, ""); + stripped_lines.push_back(std::string(line)); + } + return absl::StrJoin(stripped_lines, "\n"); +} + // Remove actual values of certain fields in a PostgreSQL Query object produced // by the PostgreSQL parser and analyzer. It is the expected input argument of // this function, pg_query. @@ -566,7 +583,8 @@ TEST_P(QueryTransformationTest, TestTransform) { // Check that the ASTs match. ASSERT_EQ(StripParseLocations(transformed_statement->DebugString()), - StripParseLocations(gsql_statement->DebugString())); + StripHasExplicitType( + StripParseLocations(gsql_statement->DebugString()))); } } diff --git a/third_party/spanner_pg/util/BUILD b/third_party/spanner_pg/util/BUILD index 83f356e1..f1279e4a 100644 --- a/third_party/spanner_pg/util/BUILD +++ b/third_party/spanner_pg/util/BUILD @@ -219,6 +219,31 @@ cc_test( ], ) +cc_library( + name = "uuid_conversion", + srcs = ["uuid_conversion.cc"], + hdrs = ["uuid_conversion.h"], + deps = [ + "//third_party/spanner_pg/postgres_includes", + "//third_party/spanner_pg/shims:error_shim", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "uuid_conversion_test", + srcs = ["uuid_conversion_test.cc"], + deps = [ + ":uuid_conversion", + ":valid_memory_context_fixture", + "//third_party/spanner_pg/src/backend:backend_with_shims", + "@com_google_googletest//:gtest_main", + "@com_google_zetasql//zetasql/base/testing:status_matchers", + ], +) + cc_library( name = "unittest_utils", testonly = True, diff --git a/third_party/spanner_pg/interface/stub_bootstrap_catalog_accessor.cc b/third_party/spanner_pg/util/uuid_conversion.cc similarity index 54% rename from third_party/spanner_pg/interface/stub_bootstrap_catalog_accessor.cc rename to third_party/spanner_pg/util/uuid_conversion.cc index a85a5cb0..be810861 100644 --- a/third_party/spanner_pg/interface/stub_bootstrap_catalog_accessor.cc +++ b/third_party/spanner_pg/util/uuid_conversion.cc @@ -29,43 +29,34 @@ // MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. //------------------------------------------------------------------------------ -#include "third_party/spanner_pg/interface/bootstrap_catalog_accessor.h" +#include "third_party/spanner_pg/util/uuid_conversion.h" -#include "absl/status/status.h" #include "absl/status/statusor.h" - -namespace postgres_translator { - -const PgBootstrapCatalog* GetPgBootstrapCatalog() { - return nullptr; -} - -absl::StatusOr GetPgCollationDataFromBootstrap( - const PgBootstrapCatalog* catalog, absl::string_view collation_name) { - return absl::UnimplementedError( - "invoked stub GetPgCollationDataFromBootstrap"); +#include "absl/strings/string_view.h" +#include "third_party/spanner_pg/postgres_includes/all.h" +#include "third_party/spanner_pg/shims/error_shim.h" +#include "zetasql/base/status_macros.h" + +absl::StatusOr UuidStringToPgConst(absl::string_view uuid_string) { + ZETASQL_ASSIGN_OR_RETURN(Datum uuid_datum, + postgres_translator::CheckedDirectFunctionCall1( + /*func=*/uuid_in, + /*arg1=*/CStringGetDatum(uuid_string.data()))); + + return postgres_translator::CheckedPgMakeConst( + /*consttype=*/UUIDOID, + /*consttypmod=*/-1, + /*constcollid=*/InvalidOid, + /*constlen=*/sizeof(pg_uuid_t), + /*constvalue=*/uuid_datum, + /*constisnull=*/false, + /*constbyval=*/false); } -absl::StatusOr GetPgNamespaceDataFromBootstrap( - const PgBootstrapCatalog* catalog, absl::string_view namespace_name) { - return absl::UnimplementedError( - "invoked stub GetPgNamespaceDataFromBootstrap"); -} +absl::StatusOr PgConstToUuidString(Const* pg_const) { + ZETASQL_ASSIGN_OR_RETURN(Datum datum, postgres_translator::CheckedDirectFunctionCall1( + /*func=*/uuid_out, + /*arg1=*/pg_const->constvalue)); -absl::StatusOr GetPgProcDataFromBootstrap( - const PgBootstrapCatalog* catalog, int64_t proc_oid) { - return absl::UnimplementedError("invoked stub GetPgProcDataFromBootstrap"); + return DatumGetCString(datum); } - -absl::StatusOr GetPgTypeDataFromBootstrap( - const PgBootstrapCatalog* catalog, absl::string_view type_name) { - return absl::UnimplementedError("invoked stub GetPgTypeDataFromBootstrap"); -} - -absl::StatusOr GetPgTypeDataFromBootstrap( - const PgBootstrapCatalog* catalog, int64_t type_oid) { - return absl::UnimplementedError("invoked stub GetPgTypeDataFromBootstrap"); -} - - -} // namespace postgres_translator diff --git a/third_party/spanner_pg/util/uuid_conversion.h b/third_party/spanner_pg/util/uuid_conversion.h new file mode 100644 index 00000000..ee78d9b8 --- /dev/null +++ b/third_party/spanner_pg/util/uuid_conversion.h @@ -0,0 +1,48 @@ +// +// PostgreSQL is released under the PostgreSQL License, a liberal Open Source +// license, similar to the BSD or MIT licenses. +// +// PostgreSQL Database Management System +// (formerly known as Postgres, then as Postgres95) +// +// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group +// +// Portions Copyright © 1994, The Regents of the University of California +// +// Portions Copyright 2023 Google LLC +// +// Permission to use, copy, modify, and distribute this software and its +// documentation for any purpose, without fee, and without a written agreement +// is hereby granted, provided that the above copyright notice and this +// paragraph and the following two paragraphs appear in all copies. +// +// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING +// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, +// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// +// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN +// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE +// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +//------------------------------------------------------------------------------ + +#ifndef UTIL_UUID_CONVERSION_H_ +#define UTIL_UUID_CONVERSION_H_ + +#include + +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "third_party/spanner_pg/postgres_includes/all.h" + +// Converts a human-readable valid UUID string (with or without dashes) to a PG +// Const using the PG uuid_in function. +absl::StatusOr UuidStringToPgConst(absl::string_view uuid_string); + +// Converts a PG Const to a human-readable UUID string using PG uuid_out +// function. +absl::StatusOr PgConstToUuidString(Const* pg_const); +#endif // UTIL_UUID_CONVERSION_H_ diff --git a/third_party/spanner_pg/util/uuid_conversion_test.cc b/third_party/spanner_pg/util/uuid_conversion_test.cc new file mode 100644 index 00000000..ff6d4f9f --- /dev/null +++ b/third_party/spanner_pg/util/uuid_conversion_test.cc @@ -0,0 +1,61 @@ +// +// PostgreSQL is released under the PostgreSQL License, a liberal Open Source +// license, similar to the BSD or MIT licenses. +// +// PostgreSQL Database Management System +// (formerly known as Postgres, then as Postgres95) +// +// Portions Copyright © 1996-2020, The PostgreSQL Global Development Group +// +// Portions Copyright © 1994, The Regents of the University of California +// +// Portions Copyright 2023 Google LLC +// +// Permission to use, copy, modify, and distribute this software and its +// documentation for any purpose, without fee, and without a written agreement +// is hereby granted, provided that the above copyright notice and this +// paragraph and the following two paragraphs appear in all copies. +// +// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING +// LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, +// EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// +// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN +// "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE +// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +//------------------------------------------------------------------------------ + +#include "third_party/spanner_pg/util/uuid_conversion.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "zetasql/base/testing/status_matchers.h" +#include "third_party/spanner_pg/util/valid_memory_context_fixture.h" + +using ValidMemoryContextUuidConversionTest = + ::postgres_translator::test::ValidMemoryContext; + +TEST_F(ValidMemoryContextUuidConversionTest, + UuidStringToPgConstAndBackToUuidString) { + std::string uuid_string = "9a31411b-caca-4ff1-86e9-39fbd2bc3f39"; + ZETASQL_ASSERT_OK_AND_ASSIGN(Const * pg_const, UuidStringToPgConst(uuid_string)); + EXPECT_NE(pg_const, nullptr); + pg_uuid_t* pg_uuid = DatumGetUUIDP(pg_const->constvalue); + + std::string uuid_bytes = + "\x9a\x31\x41\x1b\xca\xca\x4f\xf1\x86\xe9\x39\xfb\xd2\xbc\x3f\x39"; + + for (int i = 0; i < uuid_bytes.size(); i++) { + EXPECT_EQ(pg_uuid->data[i], static_cast(uuid_bytes[i])); + } + + ZETASQL_ASSERT_OK_AND_ASSIGN(absl::string_view uuid_string_from_conversion, + PgConstToUuidString(pg_const)); + EXPECT_EQ(uuid_string_from_conversion, uuid_string); +}