alternator: prevent adding GSI conflicting with a vector index

All the "indexes" we implement in Alternator - GSI, LSI and the new
vector index - share the same IndexName namespace, which we'll use in
Query to refer to the index. In the previous patch we already prevented
adding a vector index with the same name as an existing GSI or LSI.
In this patch we also prevent the reverse - adding a GSI with the name
of an existing vector index.

Additionally, one cannot add a GSI on a key that is already the key of
a vector index: The types conflict: The key of a vector index must be a
vector column, while the key of a GSI must have a standard key type
(string, binary or number).

We have tests for this later, this the big test patch.
This commit is contained in:
Nadav Har'El
2026-04-05 10:23:11 +03:00
parent 82de16f92c
commit ffe1029b7c

View File

@@ -2246,17 +2246,20 @@ future<executor::request_return_type> executor::create_table(client_state& clien
// columns of the base table or any of its prior GSIs or LSIs, the type
// given in AttributeDefinitions must match the type of the existing key -
// otherwise Alternator will not know which type to enforce in new writes.
// Also, if the table already has vector indexes, their key attributes cannot
// be redefined in AttributeDefinitions with a non-vector type.
// This function checks for such conflicts. It assumes that the structure of
// the given attribute_definitions was already validated (with
// validate_attribute_definitions()).
// This function should be called multiple times - once for the base schema
// and once for each of its views (existing GSIs and LSIs on this table).
static void check_attribute_definitions_conflicts(const rjson::value& attribute_definitions, const schema& schema) {
for (auto& def : schema.primary_key_columns()) {
std::string def_type = type_to_string(def.type);
for (auto it = attribute_definitions.Begin(); it != attribute_definitions.End(); ++it) {
const rjson::value& attribute_info = *it;
if (rjson::to_string_view(attribute_info["AttributeName"]) == def.name_as_text()) {
for (auto it = attribute_definitions.Begin(); it != attribute_definitions.End(); ++it) {
const rjson::value& attribute_info = *it;
std::string_view attribute_name = rjson::to_string_view(attribute_info["AttributeName"]);
for (auto& def : schema.primary_key_columns()) {
if (attribute_name == def.name_as_text()) {
auto def_type = type_to_string(def.type);
std::string_view type = rjson::to_string_view(attribute_info["AttributeType"]);
if (type != def_type) {
throw api_error::validation(fmt::format("AttributeDefinitions redefined {} to {} already a key attribute of type {} in this table", def.name_as_text(), type, def_type));
@@ -2264,6 +2267,12 @@ future<executor::request_return_type> executor::create_table(client_state& clien
break;
}
}
// Additionally, if we have a vector index, its key attribute is
// required to have a vector type, and cannot be listed in
// AttributeDefinitions with a non-vector key type.
if (has_vector_index_on_attribute(schema, attribute_name)) {
throw api_error::validation(fmt::format("AttributeDefinitions redefines {} but already a key of a vector index in this table", attribute_name));
}
}
}
@@ -2522,6 +2531,10 @@ future<executor::request_return_type> executor::update_table(client_state& clien
co_return api_error::validation(fmt::format(
"LSI {} already exists in table {}, can't use same name for GSI", index_name, table_name));
}
if (tab->has_index(sstring(index_name))) {
co_return api_error::validation(fmt::format(
"Vector index {} already exists in table {}, cannot reuse the name for a GSI", index_name, table_name));
}
try {
locator::assert_rf_rack_valid_keyspace(keyspace_name, p.local().local_db().get_token_metadata_ptr(),
p.local().local_db().find_keyspace(keyspace_name).get_replication_strategy());