backend/schema/updater/schema_updater_tests/named_schema.cc (664 lines of code) (raw):
//
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "backend/schema/catalog/named_schema.h"
#include <memory>
#include <string>
#include <vector>
#include "google/spanner/admin/database/v1/common.pb.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "zetasql/base/testing/status_matchers.h"
#include "tests/common/proto_matchers.h"
#include "absl/status/status.h"
#include "absl/strings/substitute.h"
#include "backend/schema/catalog/schema.h"
#include "backend/schema/catalog/sequence.h"
#include "backend/schema/catalog/table.h"
#include "backend/schema/catalog/udf.h"
#include "backend/schema/catalog/view.h"
#include "backend/schema/graph/schema_node.h"
#include "backend/schema/updater/schema_updater_tests/base.h"
#include "common/errors.h"
namespace google {
namespace spanner {
namespace emulator {
namespace backend {
namespace test {
// For the following tests, a custom PG DDL statement is required as translating
// expressions from GSQL to PG is not supported in tests.
using database_api::DatabaseDialect::POSTGRESQL;
// TODO: Add more tests once we can add tables, views, indexes, and
// sequences through SQL. We can then test adding these objects and DROP SCHEMA
// errors when the named schema still contains dependencies.
TEST_P(SchemaUpdaterTest, CreateNamedSchema_Basic) {
std::unique_ptr<const Schema> schema;
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(schema,
CreateSchema({R"(
CREATE SCHEMA mynamedschema
)"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"(
CREATE SCHEMA mynamedschema
)"}));
}
const NamedSchema* named_schema = schema->FindNamedSchema("mynamedschema");
EXPECT_NE(named_schema, nullptr);
EXPECT_EQ(named_schema->Name(), "mynamedschema");
}
TEST_P(SchemaUpdaterTest, CreateNamedSchema_NestedFails) {
// POSTGRESQL throws error on nested schema before the emulator does.
if (GetParam() == POSTGRESQL) GTEST_SKIP();
EXPECT_THAT(CreateSchema({R"(
CREATE SCHEMA mynamedschema.nested
)"}),
StatusIs(error::SchemaObjectTypeUnsupportedInNamedSchema(
"Schema", "mynamedschema.nested")));
}
TEST_P(SchemaUpdaterTest, CreateNamedSchema_ReservedSchemaNamesFailed) {
EXPECT_THAT(CreateSchema({R"(CREATE SCHEMA Net)"}),
StatusIs(error::InvalidSchemaName("Schema", "Net")));
EXPECT_THAT(
CreateSchema({R"(CREATE SCHEMA DEFINITION_SCHEMA)"}),
StatusIs(error::InvalidSchemaName("Schema", "DEFINITION_SCHEMA")));
}
TEST_P(SchemaUpdaterTest, DropNamedSchema_Success) {
std::unique_ptr<const Schema> schema;
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(schema,
CreateSchema({R"(
CREATE SCHEMA mynamedschema
)"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/true));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"(
CREATE SCHEMA mynamedschema
)"}));
}
const NamedSchema* named_schema = schema->FindNamedSchema("mynamedschema");
EXPECT_NE(named_schema, nullptr);
EXPECT_EQ(named_schema->Name(), "mynamedschema");
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(schema,
UpdateSchema(schema.get(), {R"(
DROP SCHEMA mynamedschema
)"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/true));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(schema, UpdateSchema(schema.get(), {R"(
DROP SCHEMA mynamedschema
)"}));
}
const NamedSchema* dropped_named_schema =
schema->FindNamedSchema("mynamedschema");
EXPECT_EQ(dropped_named_schema, nullptr);
}
TEST_P(SchemaUpdaterTest, DropNamedSchema_Failed) {
if (GetParam() == POSTGRESQL) {
EXPECT_THAT(CreateSchema({R"(
DROP SCHEMA mynamedschema
)"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/true),
StatusIs(error::NamedSchemaNotFound("mynamedschema")));
} else {
EXPECT_THAT(CreateSchema({R"(
DROP SCHEMA mynamedschema
)"}),
StatusIs(error::NamedSchemaNotFound("mynamedschema")));
}
}
TEST_P(SchemaUpdaterTest, TableWithNamedSchema_CreateAndDropSuccess) {
std::unique_ptr<const Schema> schema;
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
CreateSchema(
{R"(CREATE SCHEMA mynamedschema)",
R"(CREATE TABLE mynamedschema.T(col1 BIGINT PRIMARY KEY))"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
CreateSchema(
{R"(CREATE SCHEMA mynamedschema)",
R"(CREATE TABLE mynamedschema.T(col1 INT64) PRIMARY KEY(col1))"}));
}
const NamedSchema* named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
const Table* t = named_schema->FindTable("mynamedschema.T");
ASSERT_NE(t, nullptr);
EXPECT_EQ(t->Name(),
GetParam() == POSTGRESQL ? "mynamedschema.t" : "mynamedschema.T");
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(), {R"(DROP TABLE mynamedschema.T)"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(), {R"(DROP TABLE mynamedschema.T)"}));
}
named_schema = schema->FindNamedSchema("mynamedschema");
EXPECT_NE(named_schema, nullptr);
EXPECT_EQ(
named_schema->FindTable(GetParam() == POSTGRESQL ? "mynamedschema.t"
: "mynamedschema.T"),
nullptr);
EXPECT_EQ(named_schema->FindTableUsingSynonym("mynamedschema.syn"), nullptr);
}
TEST_P(SchemaUpdaterTest,
TableWithNamedSchema_CreateAndDropWithSynonymSuccess) {
// Synonyms not supported during table POSTGRESQL table creation
if (GetParam() == POSTGRESQL) GTEST_SKIP();
ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr<const Schema> schema,
CreateSchema({R"(CREATE SCHEMA mynamedschema)",
R"(
CREATE TABLE mynamedschema.T (col1 INT64, SYNONYM(`mynamedschema.syn`))
PRIMARY KEY (col1)
)"}));
const NamedSchema* named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
const Table* synonym_t =
named_schema->FindTableUsingSynonym("mynamedschema.syn");
ASSERT_NE(synonym_t, nullptr);
EXPECT_EQ(synonym_t->Name(), "mynamedschema.T");
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(), {R"(DROP TABLE mynamedschema.T)"}));
named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
EXPECT_EQ(named_schema->FindTable("mynamedschema.T"), nullptr);
EXPECT_EQ(named_schema->FindTableUsingSynonym("mynamedschema.syn"), nullptr);
}
TEST_P(SchemaUpdaterTest, TableWithNamedSchema_NoExistingSchemaFails) {
if (GetParam() == POSTGRESQL) {
EXPECT_THAT(
CreateSchema(
{R"(CREATE TABLE mynamedschema.T (col1 BIGINT PRIMARY KEY))"},
/*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false),
StatusIs(error::NamedSchemaNotFound("mynamedschema")));
} else {
EXPECT_THAT(CreateSchema({R"(CREATE TABLE mynamedschema.T (
col1 INT64,
) PRIMARY KEY(col1))"}),
StatusIs(error::NamedSchemaNotFound("mynamedschema")));
}
}
TEST_P(SchemaUpdaterTest, TableWithNamedSchema_AddAndDropSynonym) {
std::unique_ptr<const Schema> schema;
std::string table_name =
GetParam() == POSTGRESQL ? "mynamedschema.t" : "mynamedschema.T";
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
CreateSchema(
{
R"(CREATE SCHEMA mynamedschema)",
absl::Substitute(R"(CREATE TABLE $0 (col1 BIGINT primary key))",
table_name),
},
/*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
CreateSchema({R"(CREATE SCHEMA mynamedschema)",
absl::Substitute(
R"(CREATE TABLE $0 (col1 INT64) PRIMARY KEY (col1))",
table_name)}));
}
const NamedSchema* named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
ASSERT_NE(named_schema->FindTable(table_name), nullptr);
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
UpdateSchema(
schema.get(),
{absl::Substitute("ALTER TABLE $0 ADD SYNONYM syn", table_name)},
/*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(),
{absl::Substitute("ALTER TABLE $0 ADD SYNONYM syn",
table_name)}));
}
named_schema = schema->FindNamedSchema("mynamedschema");
const Table* synonym_t = schema->FindTableUsingSynonym("syn");
EXPECT_NE(synonym_t, nullptr);
EXPECT_EQ(synonym_t->Name(), table_name);
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(),
{absl::Substitute("ALTER TABLE $0 DROP SYNONYM syn",
table_name)}));
named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema->FindTable(table_name), nullptr);
EXPECT_EQ(named_schema->FindTableUsingSynonym("syn"), nullptr);
}
TEST_P(SchemaUpdaterTest, ViewWithNamedSchema_CreateAndDropSuccess) {
std::unique_ptr<const Schema> schema;
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
CreateSchema(
{R"(CREATE SCHEMA mynamedschema)",
R"(
CREATE TABLE mynamedschema.T (
col1 bigint primary key,
col2 varchar
)
)",
R"(CREATE OR REPLACE VIEW mynamedschema.V SQL SECURITY INVOKER AS
SELECT t.col1, t.col2 FROM mynamedschema.T as t)"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"(CREATE SCHEMA mynamedschema)",
R"(
CREATE TABLE mynamedschema.T (
col1 INT64,
col2 STRING(MAX)
) PRIMARY KEY(col1)
)",
R"(
CREATE OR REPLACE VIEW `mynamedschema.V` SQL SECURITY INVOKER AS
SELECT t.col1, t.col2 FROM mynamedschema.T as t
)"}));
}
const NamedSchema* named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
const View* v = named_schema->FindView("mynamedschema.V");
ASSERT_NE(v, nullptr);
EXPECT_EQ(v->Name(),
GetParam() == POSTGRESQL ? "mynamedschema.v" : "mynamedschema.V");
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(), {"DROP VIEW mynamedschema.V"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(), {"DROP VIEW mynamedschema.V"}));
}
named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
ASSERT_EQ(
named_schema->FindView(GetParam() == POSTGRESQL ? "mynamedschema.v"
: "mynamedschema.V"),
nullptr);
}
TEST_P(SchemaUpdaterTest, ViewWithNamedSchema_NoExistingSchemaFails) {
if (GetParam() == POSTGRESQL) GTEST_SKIP();
ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr<const Schema> schema, CreateSchema({R"(
CREATE TABLE T (
col1 INT64,
col2 STRING(MAX)
) PRIMARY KEY(col1)
)"}));
EXPECT_THAT(UpdateSchema(schema.get(),
{
R"(
CREATE OR REPLACE VIEW `mynamedschema.V` SQL SECURITY INVOKER AS
SELECT t.col1, t.col2 FROM T as t ORDER BY t.col2
)"}),
StatusIs(error::NamedSchemaNotFound("mynamedschema")));
}
TEST_P(SchemaUpdaterTest, SequenceWithNamedSchema_CreateAndDropSuccess) {
std::unique_ptr<const Schema> schema;
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
CreateSchema(
{R"(CREATE SCHEMA mynamedschema)",
R"(CREATE SEQUENCE mynamedschema.myseq BIT_REVERSED_POSITIVE)",
R"(CREATE TABLE mynamedschema.T (
col1 bigint DEFAULT nextval('mynamedschema.myseq') PRIMARY KEY)
)"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, CreateSchema({R"(CREATE SCHEMA mynamedschema)",
R"(CREATE SEQUENCE mynamedschema.myseq OPTIONS (
sequence_kind = "bit_reversed_positive"
))",
R"(CREATE TABLE mynamedschema.T (
col1 INT64 DEFAULT (
GET_NEXT_SEQUENCE_VALUE(SEQUENCE mynamedschema.myseq)
)) PRIMARY KEY (col1)
)"}));
}
const NamedSchema* named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
const Sequence* sequence = named_schema->FindSequence("mynamedschema.myseq");
ASSERT_NE(sequence, nullptr);
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(schema,
UpdateSchema(schema.get(),
{R"(DROP TABLE mynamedschema.T)",
R"(DROP SEQUENCE mynamedschema.myseq)"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
UpdateSchema(schema.get(), {R"(DROP TABLE mynamedschema.T)",
R"(DROP SEQUENCE mynamedschema.myseq)"}));
}
named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
EXPECT_EQ(named_schema->FindSequence("mynamedschema.myseq"), nullptr);
}
TEST_P(SchemaUpdaterTest, SequenceWithNamedSchema_CrossSchemaSuccess) {
std::unique_ptr<const Schema> schema;
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
CreateSchema(
{R"(CREATE SCHEMA mynamedschema1)",
R"(CREATE SCHEMA mynamedschema2)",
R"(CREATE SEQUENCE mynamedschema1.myseq BIT_REVERSED_POSITIVE)",
R"(CREATE TABLE mynamedschema2.T (
col1 bigint DEFAULT nextval('mynamedschema1.myseq') PRIMARY KEY)
)"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, CreateSchema({R"(CREATE SCHEMA mynamedschema1)",
R"(CREATE SCHEMA mynamedschema2)",
R"(CREATE SEQUENCE mynamedschema1.myseq OPTIONS (
sequence_kind = "bit_reversed_positive"))",
R"(CREATE TABLE mynamedschema2.T (
col1 INT64 DEFAULT (
GET_NEXT_SEQUENCE_VALUE(SEQUENCE mynamedschema1.myseq)
)) PRIMARY KEY (col1))"}));
}
const NamedSchema* named_schema_1 = schema->FindNamedSchema("mynamedschema1");
ASSERT_NE(named_schema_1, nullptr);
const NamedSchema* named_schema_2 = schema->FindNamedSchema("mynamedschema2");
ASSERT_NE(named_schema_2, nullptr);
ASSERT_NE(named_schema_2->FindTable("mynamedschema2.T"), nullptr);
const Sequence* sequence =
named_schema_1->FindSequence("mynamedschema1.myseq");
ASSERT_NE(sequence, nullptr);
}
TEST_P(SchemaUpdaterTest, CreateSequenceWithNamedSchema_NoExistingSchemaFails) {
std::unique_ptr<const Schema> schema;
if (GetParam() == POSTGRESQL) {
EXPECT_THAT(
CreateSchema(
{R"(CREATE SEQUENCE mynamedschema.myseq BIT_REVERSED_POSITIVE)"},
/*proto_descriptor_bytes=*/"", /*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false),
StatusIs(error::NamedSchemaNotFound("mynamedschema")));
} else {
EXPECT_THAT(CreateSchema({R"(CREATE SEQUENCE mynamedschema.myseq OPTIONS (
sequence_kind = "bit_reversed_positive"
))"}),
StatusIs(error::NamedSchemaNotFound("mynamedschema")));
}
}
TEST_P(SchemaUpdaterTest, IndexWithNamedSchema_CreateAndDropSuccess) {
std::unique_ptr<const Schema> schema;
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema,
CreateSchema(
{R"(CREATE SCHEMA mynamedschema)",
R"(CREATE TABLE mynamedschema.T (col1 bigint primary key, col2 varchar))",
R"(CREATE INDEX idx1 ON mynamedschema.T(col2))"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(schema, CreateSchema({R"(CREATE SCHEMA mynamedschema)",
R"(
CREATE TABLE mynamedschema.T (
col1 INT64,
col2 STRING(MAX)
) PRIMARY KEY(col1)
)",
R"(
CREATE INDEX mynamedschema.idx1 ON mynamedschema.T(col2)
)"}));
}
const NamedSchema* named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
const Index* index = named_schema->FindIndex("mynamedschema.idx1");
ASSERT_NE(index, nullptr);
EXPECT_EQ(index->Name(), "mynamedschema.idx1");
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(), {"DROP INDEX mynamedschema.idx1"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(), {"DROP INDEX mynamedschema.idx1"}));
}
named_schema = schema->FindNamedSchema("mynamedschema");
ASSERT_NE(named_schema, nullptr);
EXPECT_EQ(named_schema->FindIndex("mynamedschema.idx1"), nullptr);
}
TEST_P(SchemaUpdaterTest, IndexWithNamedSchema_CrossSchemaFails) {
if (GetParam() == POSTGRESQL) {
EXPECT_THAT(
CreateSchema(
{R"(CREATE SCHEMA mynamedschema1)",
R"(CREATE SCHEMA mynamedschema2)",
R"(CREATE TABLE mynamedschema1.T (col1 bigint primary key, col2 varchar))",
R"(CREATE INDEX mynamedschema.idx1 ON mynamedschema1.T(col2))"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false),
::zetasql_base::testing::StatusIs(
absl::StatusCode::kInvalidArgument,
::testing::HasSubstr("syntax error at or near \".\";")));
EXPECT_THAT(
CreateSchema(
{R"(CREATE SCHEMA mynamedschema)",
R"(CREATE TABLE T (col1 bigint primary key, col2 varchar))",
R"(CREATE INDEX mynamedschema.idx1 ON T(col2))"},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false),
::zetasql_base::testing::StatusIs(
absl::StatusCode::kInvalidArgument,
::testing::HasSubstr("syntax error at or near \".\";")));
} else {
EXPECT_THAT(
CreateSchema(
{R"(CREATE SCHEMA mynamedschema1)",
R"(CREATE SCHEMA mynamedschema2)",
R"(CREATE TABLE mynamedschema1.T (col1 INT64, col2 STRING(MAX))
PRIMARY KEY(col1))",
R"(CREATE INDEX mynamedschema2.idx1 ON mynamedschema1.T(col2))"}),
StatusIs(error::IndexInDifferentSchema("mynamedschema2.idx1",
"mynamedschema1.T")));
EXPECT_THAT(
CreateSchema(
{R"(CREATE SCHEMA mynamedschema)",
R"(CREATE TABLE mynamedschema.T (col1 INT64, col2 STRING(MAX))
PRIMARY KEY(col1))",
R"(CREATE INDEX idx1 ON mynamedschema.T(col2))"}),
StatusIs(error::IndexInDifferentSchema("idx1", "mynamedschema.T")));
EXPECT_THAT(
CreateSchema(
{R"(CREATE SCHEMA mynamedschema)",
R"(CREATE TABLE T (col1 INT64, col2 STRING(MAX)) PRIMARY KEY(col1))",
R"(CREATE INDEX mynamedschema.idx1 ON T(col2))"}),
StatusIs(error::IndexInDifferentSchema("mynamedschema.idx1", "T")));
}
}
TEST_P(SchemaUpdaterTest, IndexWithNamedSchema_NoExistingSchemaFails) {
// POSTGRESQL creates indexes in the named schemas of their tables.
if (GetParam() == POSTGRESQL) GTEST_SKIP();
ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr<const Schema> schema, CreateSchema({R"(
CREATE TABLE T (
col1 INT64,
col2 STRING(MAX)
) PRIMARY KEY(col1)
)"}));
EXPECT_THAT(UpdateSchema(schema.get(), {R"(
CREATE INDEX mynamedschema.idx1 ON T(col2)
)"}),
StatusIs(error::NamedSchemaNotFound("mynamedschema")));
}
TEST_P(SchemaUpdaterTest, IndexWithNamedSchema_DuplicateIndexFails) {
if (GetParam() == POSTGRESQL) {
ZETASQL_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<const Schema> schema,
CreateSchema(
{
R"(CREATE SCHEMA mynamedschema)",
R"(CREATE TABLE mynamedschema.t (col1 bigint primary key, col2 varchar))",
R"(CREATE INDEX idx1 ON mynamedschema.t (col2))",
},
/*proto_descriptor_bytes=*/"",
/*dialect=*/POSTGRESQL,
/*use_gsql_to_pg_translation=*/false));
EXPECT_THAT(UpdateSchema(schema.get(),
{"CREATE INDEX idx1 ON mynamedschema.t (col2)"}),
StatusIs(error::SchemaObjectAlreadyExists(
"Index", "mynamedschema.idx1")));
} else {
ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr<const Schema> schema,
CreateSchema({R"(CREATE SCHEMA mynamedschema)",
R"(
CREATE TABLE mynamedschema.T (
col1 INT64,
col2 STRING(MAX)
) PRIMARY KEY(col1)
)",
R"(
CREATE INDEX mynamedschema.idx1 ON mynamedschema.T(col2)
)"}));
EXPECT_THAT(
UpdateSchema(
schema.get(),
{"CREATE INDEX mynamedschema.idx1 ON mynamedschema.T(col2)"}),
StatusIs(
error::SchemaObjectAlreadyExists("Index", "mynamedschema.idx1")));
}
}
TEST_P(SchemaUpdaterTest, UDFWithNamedSchema_CreateAndDropSuccess) {
if (GetParam() == POSTGRESQL) GTEST_SKIP();
ZETASQL_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<const Schema> schema,
CreateSchema({
R"(CREATE SCHEMA my_schema)",
R"(CREATE FUNCTION my_schema.udf1() RETURNS INT64 SQL SECURITY INVOKER AS (1))",
}));
const NamedSchema* named_schema = schema->FindNamedSchema("my_schema");
ASSERT_NE(named_schema, nullptr);
const Udf* udf = named_schema->FindUdf("my_schema.udf1");
ASSERT_NE(udf, nullptr);
EXPECT_EQ(udf->Name(), "my_schema.udf1");
EXPECT_THAT(
UpdateSchema(
schema.get(),
{R"(CREATE FUNCTION my_schema.udf1() RETURNS INT64 SQL SECURITY INVOKER AS (2))"}),
StatusIs(error::SchemaObjectAlreadyExists("Function", "my_schema.udf1")));
ZETASQL_ASSERT_OK_AND_ASSIGN(
schema, UpdateSchema(schema.get(), {"DROP FUNCTION my_schema.udf1"}));
named_schema = schema->FindNamedSchema("my_schema");
ASSERT_NE(named_schema, nullptr);
EXPECT_EQ(named_schema->FindUdf("my_schema.udf1"), nullptr);
}
TEST_P(SchemaUpdaterTest, UDFWithNamedSchema_SameNameInDifferentNamedSchemas) {
if (GetParam() == POSTGRESQL) GTEST_SKIP();
ZETASQL_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<const Schema> schema,
CreateSchema({
R"(CREATE SCHEMA schema1)",
R"(CREATE SCHEMA schema2)",
R"(CREATE FUNCTION schema1.udf1(x INT64) RETURNS INT64 SQL SECURITY
INVOKER AS (x + 1))",
R"(CREATE FUNCTION schema2.udf1(x INT64) RETURNS INT64 SQL SECURITY
INVOKER AS (x + 2))",
}));
const NamedSchema* schema1 = schema->FindNamedSchema("schema1");
ASSERT_NE(schema1, nullptr);
const Udf* udf1_schema1 = schema1->FindUdf("schema1.udf1");
ASSERT_NE(udf1_schema1, nullptr);
ASSERT_EQ(udf1_schema1->Name(), "schema1.udf1");
const NamedSchema* schema2 = schema->FindNamedSchema("schema2");
ASSERT_NE(schema2, nullptr);
const Udf* udf1_schema2 = schema2->FindUdf("schema2.udf1");
ASSERT_NE(udf1_schema2, nullptr);
ASSERT_EQ(udf1_schema2->Name(), "schema2.udf1");
EXPECT_NE(udf1_schema1, udf1_schema2);
}
TEST_P(SchemaUpdaterTest, UDFWithNamedSchema_ReferencingUDFInNamedSchemas) {
if (GetParam() == POSTGRESQL) GTEST_SKIP();
ZETASQL_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<const Schema> schema,
CreateSchema(
{R"(CREATE SCHEMA schema1)", R"(CREATE SCHEMA schema2)",
R"(CREATE FUNCTION schema2.udf2(x INT64) RETURNS INT64 SQL SECURITY
INVOKER AS (x + 2))",
R"(CREATE FUNCTION schema1.udf1(x INT64) RETURNS INT64 SQL SECURITY
INVOKER AS (schema2.udf2(x) + 1))",
R"(CREATE FUNCTION schema1.udf2(x INT64) RETURNS INT64 SQL SECURITY
INVOKER AS (schema1.udf1(x) + 1))"}));
const NamedSchema* schema1 = schema->FindNamedSchema("schema1");
ASSERT_NE(schema1, nullptr);
const Udf* udf1_schema1 = schema1->FindUdf("schema1.udf1");
ASSERT_NE(udf1_schema1, nullptr);
const NamedSchema* schema2 = schema->FindNamedSchema("schema2");
ASSERT_NE(schema2, nullptr);
const Udf* udf2_schema2 = schema2->FindUdf("schema2.udf2");
ASSERT_NE(udf2_schema2, nullptr);
EXPECT_EQ(udf1_schema1->dependencies().size(), 1);
EXPECT_THAT(udf1_schema1->dependencies(),
testing::UnorderedElementsAreArray(
std::vector<const SchemaNode*>{udf2_schema2}));
const Udf* udf2_schema1 = schema1->FindUdf("schema1.udf2");
ASSERT_NE(udf2_schema1, nullptr);
EXPECT_EQ(udf2_schema1->dependencies().size(), 1);
EXPECT_THAT(udf2_schema1->dependencies(),
testing::UnorderedElementsAreArray(
std::vector<const SchemaNode*>{udf1_schema1}));
EXPECT_THAT(UpdateSchema(schema.get(), {R"(DROP FUNCTION schema2.udf2)"}),
StatusIs(error::InvalidDropDependentFunction(
"UDF", "schema2.udf2", "schema1.udf1")));
}
TEST_P(SchemaUpdaterTest,
UDFWithNamedSchema_ReferencingUDFWithoutSchemaQualification) {
if (GetParam() == POSTGRESQL) GTEST_SKIP();
ZETASQL_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<const Schema> schema,
CreateSchema({
R"(CREATE SCHEMA my_schema)",
R"(CREATE FUNCTION my_schema.udf1(x INT64) RETURNS INT64 SQL
SECURITY INVOKER AS (x + 1))",
}));
const NamedSchema* my_schema = schema->FindNamedSchema("my_schema");
ASSERT_NE(my_schema, nullptr);
const Udf* udf1 = my_schema->FindUdf("my_schema.udf1");
ASSERT_NE(udf1, nullptr);
EXPECT_EQ(udf1->Name(), "my_schema.udf1");
EXPECT_THAT(
UpdateSchema(
schema.get(),
{R"(CREATE FUNCTION my_schema.udf2(x INT64) RETURNS INT64 SQL SECURITY
INVOKER AS (udf1(x) * 2))"}),
zetasql_base::testing::StatusIs(
absl::StatusCode::kInvalidArgument,
testing::HasSubstr("Function not found: udf1")));
}
} // namespace test
} // namespace backend
} // namespace emulator
} // namespace spanner
} // namespace google