in backend/schema/validators/column_validator.cc [302:473]
absl::Status ColumnValidator::ValidateUpdate(const Column* column,
const Column* old_column,
SchemaValidationContext* context) {
// if column has row deletion policy, then can't delete the column or change
// type.
bool has_row_deletion_policy =
column->table()->row_deletion_policy().has_value() &&
column->table()->row_deletion_policy()->column_name() == column->Name();
if (has_row_deletion_policy && !column->table_->is_deleted() &&
(column->is_deleted() || !column->GetType()->IsTimestamp())) {
return error::RowDeletionPolicyWillBreak(column->Name(),
column->table()->Name());
}
if (!column->change_streams_explicitly_tracking_column().empty() &&
column->is_deleted()) {
std::string change_stream_names;
for (int i = 0;
i < column->change_streams_explicitly_tracking_column().size(); ++i) {
change_stream_names.append(
column->change_streams_explicitly_tracking_column()[i]->Name());
}
return error::DropColumnWithChangeStream(
column->table()->Name(), column->Name(),
column->change_streams_explicitly_tracking_column().size(),
change_stream_names);
}
if (column->is_deleted()) {
return absl::OkStatus();
}
// Once set, column ID should never change.
ZETASQL_RET_CHECK_EQ(column->id(), old_column->id());
// For a non-deleted column, the objects it depends on should
// also be alive.
ZETASQL_RET_CHECK(!column->table_->is_deleted());
// It is invalid to drop a column which is referenced by a generated column.
for (const Column* dep : column->dependent_columns()) {
if (dep->is_deleted()) {
return error::InvalidDropColumnReferencedByGeneratedColumn(
dep->Name(), column->table()->Name(), column->Name());
}
}
if (column->is_generated() && !old_column->is_generated()) {
return error::CannotConvertRegularColumnToGeneratedColumn(
column->table()->Name(), column->Name());
}
if (!column->is_generated() && old_column->is_generated()) {
return error::CannotConvertGeneratedColumnToRegularColumn(
column->table()->Name(), column->Name());
}
if (column->is_generated() && old_column->is_generated()) {
if (!column->GetType()->Equals(old_column->GetType())) {
return error::CannotAlterStoredGeneratedColumnDataType(
column->table()->Name(), column->Name());
}
if (column->expression().value() != old_column->expression().value()) {
return error::CannotAlterGeneratedColumnExpression(
column->table()->Name(), column->Name());
}
if (column->is_stored() != old_column->is_stored()) {
return error::CannotAlterGeneratedColumnStoredAttribute(
column->table()->Name(), column->Name());
}
}
if (!column->GetType()->Equals(old_column->GetType())) {
for (const Column* generated_column : column->table()->columns()) {
if (generated_column->is_generated()) {
for (const Column* dep : generated_column->dependent_columns()) {
if (column == dep) {
return error::
CannotAlterColumnDataTypeWithDependentStoredGeneratedColumn(
column->Name());
}
}
}
}
}
if (column->source_column_) {
// There is no valid scenario under which a source column drop should
// trigger a cascading drop on referencing column.
if (column->source_column_->is_deleted()) {
ZETASQL_RET_CHECK_NE(column->table_->owner_index(), nullptr);
return error::InvalidDropColumnWithDependency(
column->name_, column->table_->owner_index()->indexed_table()->Name(),
column->table_->owner_index()->Name());
}
}
if (old_column->is_nullable_ && !column->is_nullable_) {
context->AddAction([old_column](const SchemaValidationContext* context) {
return VerifyColumnNotNull(old_column->table(), old_column, context);
});
}
// Check for size reduction and type change.
ZETASQL_RETURN_IF_ERROR(CheckAllowedColumnTypeChange(
old_column, column, old_column->GetType(), column->type_, context));
if (column->type_->IsTimestamp()) {
if (column->allows_commit_timestamp() &&
!old_column->allows_commit_timestamp()) {
context->AddAction([column](const SchemaValidationContext* context) {
return VerifyColumnCommitTimestamp(column->table_, column, context);
});
}
}
for (const SchemaNode* dependency : column->sequences_used()) {
// Cannot drop a sequence if a column depends on it.
if (dependency->is_deleted()) {
const auto& dep_info = dependency->GetSchemaNameInfo();
std::string dependency_type =
(dep_info->global ? absl::AsciiStrToUpper(dep_info->kind)
: absl::AsciiStrToLower(dep_info->kind));
return error::InvalidDropDependentColumn(dependency_type, dep_info->name,
column->FullName());
}
}
for (const SchemaNode* dependency : column->udf_dependencies()) {
if (dependency->is_deleted()) {
const auto& dep_info = dependency->GetSchemaNameInfo();
std::string dependency_type =
(dep_info->global ? absl::AsciiStrToUpper(dep_info->kind)
: absl::AsciiStrToLower(dep_info->kind));
return error::InvalidDropDependentColumn(dependency_type, dep_info->name,
column->FullName());
}
}
if (context->is_postgresql_dialect()) {
// Default and generated columns must have OIDs.
if (old_column->is_generated() || old_column->has_default_value()) {
ZETASQL_RET_CHECK(old_column->postgresql_oid().has_value());
}
if (column->is_generated() || column->has_default_value()) {
ZETASQL_RET_CHECK(column->postgresql_oid().has_value());
}
// Alter statement may change the default value which would be assigned a
// new OID so don't assert that the OIDs are the same.
} else {
ZETASQL_RET_CHECK(!old_column->postgresql_oid().has_value());
ZETASQL_RET_CHECK(!column->postgresql_oid().has_value());
}
for (const SchemaNode* dep : column->udf_dependencies()) {
// TODO When dropping support is added, a check should be added
// to ensure that this column references a UDF that is also being dropped.
if (context->IsModifiedNode(dep)) {
const auto& dep_info = dep->GetSchemaNameInfo();
std::string dependency_type =
(dep_info->global ? absl::AsciiStrToUpper(dep_info->kind)
: absl::AsciiStrToLower(dep_info->kind));
std::string modify_action = absl::StrCat("alter ", dependency_type);
std::string dependency_name;
if (auto dep_udf = dep->As<const Udf>(); dep_udf != nullptr) {
dependency_name = dep_udf->Name();
}
// No need to check modifications on index dependencies as indexes
// cannot currently be altered.
ZETASQL_RETURN_IF_ERROR(ValidateColumnSignatureChange(
modify_action, dependency_name, column, column->table(),
context->tmp_new_schema(), context->type_factory()));
}
}
return absl::OkStatus();
}