in backend/schema/updater/schema_updater.cc [3806:3911]
absl::StatusOr<const Index*> SchemaUpdaterImpl::CreateIndexHelper(
const std::string& index_name, const std::string& index_base_name,
bool is_unique, bool is_null_filtered,
const std::string* interleave_in_table,
const std::vector<ddl::KeyPartClause>& table_pk,
const ::google::protobuf::RepeatedPtrField<ddl::StoredColumnDefinition>&
stored_columns,
bool is_search_index, bool is_vector_index,
const ::google::protobuf::RepeatedPtrField<ddl::KeyPartClause>* partition_by,
const ::google::protobuf::RepeatedPtrField<ddl::KeyPartClause>* order_by,
const ::google::protobuf::RepeatedPtrField<std::string>* null_filtered_columns,
const ::google::protobuf::RepeatedPtrField<ddl::SetOption>* set_options,
const Table* indexed_table) {
if (indexed_table == nullptr) {
indexed_table = latest_schema_->FindTableCaseSensitive(index_base_name);
if (indexed_table == nullptr) {
return error::TableNotFound(index_base_name);
}
}
if (latest_schema_->num_index() >= limits::kMaxIndexesPerDatabase) {
return error::TooManyIndicesPerDatabase(index_name,
limits::kMaxIndexesPerDatabase);
}
// Tables and indexes share a namespace.
ZETASQL_RETURN_IF_ERROR(global_names_.AddName("Index", index_name));
Index::Builder builder;
std::optional<uint32_t> oid = pg_oid_assigner_->GetNextPostgresqlOid();
builder.set_postgresql_oid(oid);
if (oid.has_value()) {
ZETASQL_VLOG(2) << "Assigned oid " << oid.value() << " to index " << index_name;
}
builder.set_name(index_name);
builder.set_unique(is_unique);
builder.set_null_filtered(is_null_filtered);
ColumnsUsedByIndex columns_used_by_index;
ZETASQL_ASSIGN_OR_RETURN(
std::unique_ptr<const Table> data_table,
CreateIndexDataTable(index_name, table_pk, interleave_in_table,
stored_columns, partition_by, order_by,
null_filtered_columns, builder.get(), indexed_table,
&columns_used_by_index));
builder.set_index_data_table(data_table.get());
for (const KeyColumn* key_col : columns_used_by_index.index_key_columns) {
builder.add_key_column(key_col);
}
for (const Column* col : columns_used_by_index.stored_columns) {
builder.add_stored_column(col);
}
for (const Column* col : columns_used_by_index.null_filtered_columns) {
builder.add_null_filtered_column(col);
}
if (is_search_index) {
builder.set_index_type(is_search_index);
for (const Column* col : columns_used_by_index.partition_by_columns) {
builder.add_partition_by_column(col);
}
for (const Column* col : columns_used_by_index.order_by_columns) {
builder.add_order_by_column(col);
}
}
if (is_vector_index) {
builder.set_vector_index_type(is_vector_index);
ZETASQL_RETURN_IF_ERROR(SetVectorIndexOptions(index_name, *set_options, &builder));
}
ZETASQL_RETURN_IF_ERROR(AlterNode<Table>(
indexed_table, [&builder](Table::Editor* table_editor) -> absl::Status {
table_editor->add_index(builder.get());
builder.set_indexed_table(table_editor->get());
return absl::OkStatus();
}));
// Register a backfill action for the index.
const Index* index = builder.get();
if (!is_search_index && !is_vector_index) {
statement_context_->AddAction(
[index](const SchemaValidationContext* context) {
return BackfillIndex(index, context);
});
}
if (SDLObjectName::IsFullyQualifiedName(index_name)) {
ZETASQL_RETURN_IF_ERROR(AlterInNamedSchema(
index_name, [&builder](NamedSchema::Editor* editor) -> absl::Status {
editor->add_index(builder.get());
return absl::OkStatus();
}));
}
if (set_options != nullptr && !set_options->empty()) {
ZETASQL_RETURN_IF_ERROR(SetIndexOptions(*set_options, &builder, is_vector_index));
}
// The data table must be added after the index for correct order of
// validation.
ZETASQL_RETURN_IF_ERROR(AddNode(builder.build()));
ZETASQL_RETURN_IF_ERROR(AddNode(std::move(data_table)));
return index;
}