in backend/schema/validators/table_validator.cc [390:511]
absl::Status TableValidator::ValidateUpdate(const Table* table,
const Table* old_table,
SchemaValidationContext* context) {
if (table->is_deleted()) {
ZETASQL_RET_CHECK(!table->owner_index_ || table->owner_index_->is_deleted());
if (!table->child_tables_.empty()) {
// Build a sorted list of interleaved child tables and indexes.
std::vector<std::string> interleaved_tables;
std::vector<std::string> interleaved_indices;
for (const auto& entry : table->child_tables_) {
if (entry->owner_index()) {
interleaved_indices.push_back(entry->owner_index()->Name());
} else {
interleaved_tables.push_back(entry->Name());
}
}
std::sort(interleaved_tables.begin(), interleaved_tables.end());
std::sort(interleaved_indices.begin(), interleaved_indices.end());
// Cannot drop a table with interleaved child tables or indexes.
if (!interleaved_tables.empty()) {
return error::DropTableWithInterleavedTables(
table->name_, absl::StrJoin(interleaved_tables, ","));
} else if (!interleaved_indices.empty()) {
return error::DropTableWithDependentIndices(
table->name_, absl::StrJoin(interleaved_indices, ","));
}
}
if (!table->indexes_.empty()) {
return error::DropTableWithDependentIndices(
table->name_,
absl::StrJoin(table->indexes_.begin(), table->indexes_.end(), ",",
[](std::string* out, const Index* child) {
return out->append(child->Name());
}));
}
if (!table->change_streams_explicitly_tracking_table().empty()) {
return error::DropTableWithDependentChangeStreams(
table->name_,
absl::StrJoin(table->change_streams().begin(),
table->change_streams().end(), ",",
[](std::string* out, const ChangeStream* child) {
return out->append(child->Name());
}));
}
context->global_names()->RemoveName(table->Name());
if (!table->synonym().empty()) {
context->global_names()->RemoveName(table->synonym());
}
return absl::OkStatus();
}
// ID should not change during cloning, but the name can.
ZETASQL_RET_CHECK_EQ(table->id(), old_table->id());
if (table->is_public() && context->is_postgresql_dialect()) {
ZETASQL_RET_CHECK(table->postgresql_oid().has_value());
ZETASQL_RET_CHECK(old_table->postgresql_oid().has_value());
ZETASQL_RET_CHECK_EQ(table->postgresql_oid().value(),
old_table->postgresql_oid().value());
} else {
ZETASQL_RET_CHECK(!table->postgresql_oid().has_value());
ZETASQL_RET_CHECK(!old_table->postgresql_oid().has_value());
}
if (table->owner_change_stream_) {
ZETASQL_RET_CHECK(!table->owner_change_stream_->is_deleted());
}
if (table->owner_index_) {
ZETASQL_RET_CHECK(!table->owner_index_->is_deleted());
}
// Check additional constraints on new columns.
for (const Column* column : table->columns()) {
// Ignore old columns.
if (old_table->FindColumn(column->Name()) != nullptr) {
continue;
}
// New columns cannot be nullable unless it is a generated column or
// it has a default value. Index stored columns of the index data table
// could be as non nullable as the source table is, but these checks
// (nullable, generated, default value) do not apply.
if (!table->owner_index_ && !column->is_nullable() &&
!column->is_generated() && !column->has_default_value()) {
return error::AddingNotNullColumn(table->name_, column->Name());
}
}
// Cannot drop key columns, change their order or nullability.
ZETASQL_RET_CHECK_EQ(table->primary_key_.size(), old_table->primary_key_.size());
for (int i = 0; i < table->primary_key_.size(); ++i) {
if (table->primary_key_[i]->is_deleted()) {
return error::InvalidDropKeyColumn(
table->primary_key_[i]->column()->Name(), table->name_);
}
ZETASQL_RET_CHECK_EQ(table->primary_key_[i]->is_descending(),
old_table->primary_key()[i]->is_descending());
if (table->primary_key_[i]->column()->is_nullable() !=
old_table->primary_key()[i]->column()->is_nullable()) {
std::string reason = absl::Substitute(
"from $0 to $1",
old_table->primary_key()[i]->column()->is_nullable() ? "NULL"
: "NOT NULL",
table->primary_key_[i]->column()->is_nullable() ? "NULL"
: "NOT NULL");
return error::CannotChangeKeyColumn(
absl::StrCat(table->name_, ".",
table->primary_key_[i]->column()->Name()),
reason);
}
}
if (!table->synonym().empty() && !old_table->synonym().empty() &&
table->synonym() != old_table->synonym()) {
return error::CannotAlterSynonym(table->synonym(), table->name_);
}
ZETASQL_RETURN_IF_ERROR(ValidateUpdateRowDeletionPolicy(table, old_table));
return absl::OkStatus();
}