diff --git a/cql3/statements/alter_keyspace_statement.cc b/cql3/statements/alter_keyspace_statement.cc index e4bf53683adb..b84e094b8e82 100644 --- a/cql3/statements/alter_keyspace_statement.cc +++ b/cql3/statements/alter_keyspace_statement.cc @@ -9,7 +9,9 @@ */ #include +#include #include +#include #include "alter_keyspace_statement.hh" #include "prepared_statement.hh" #include "service/migration_manager.hh" @@ -40,10 +42,21 @@ future<> cql3::statements::alter_keyspace_statement::check_access(query_processo return state.has_keyspace_access(_name, auth::permission::ALTER); } +static bool validate_rf_difference(const std::string_view curr_rf, const std::string_view new_rf) { + auto to_number = [] (const std::string_view rf) { + int result; + // We assume the passed string view represents a valid decimal number, + // so we don't need the error code. + (void) std::from_chars(rf.begin(), rf.end(), result); + return result; + }; + + // We want to ensure that each DC's RF is going to change by at most 1 + // because in that case the old and new quorums must overlap. + return std::abs(to_number(curr_rf) - to_number(new_rf)) <= 1; +} + void cql3::statements::alter_keyspace_statement::validate(query_processor& qp, const service::client_state& state) const { - // TODO: when processing a tablet-enabled keyspace, - // we must check if the new RF differs by at most 1 from the old RF, - // and fail the query if that's not the case auto tmp = _name; std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); if (is_system_keyspace(tmp)) { @@ -57,6 +70,17 @@ void cql3::statements::alter_keyspace_statement::validate(query_processor& qp, c } try { auto ks = qp.db().find_keyspace(_name); + + if (ks.get_replication_strategy().uses_tablets()) { + const std::map& current_rfs = ks.metadata()->strategy_options(); + for (const auto& [new_dc, new_rf] : _attrs->get_replication_options()) { + auto it = current_rfs.find(new_dc); + if (it != current_rfs.end() && !validate_rf_difference(it->second, new_rf)) { + throw exceptions::invalid_request_exception("Cannot modify replication factor of any DC by more than 1 at a time."); + } + } + } + data_dictionary::storage_options current_options = ks.metadata()->get_storage_options(); data_dictionary::storage_options new_options = _attrs->get_storage_options(); if (!qp.proxy().features().keyspace_storage_options && !new_options.is_local_type()) {