Files
scylladb/cql3/constants.cc
Nadav Har'El 3c6931c1ed cql3: let constants::setter evaluate expressions using prefetched row data
Previously, constants::setter evaluated its expression using only the query
options, which means expressions referencing row columns (column_value nodes)
would crash or return incorrect results.

Add evaluate_on_prefetched_row() to update_parameters: it evaluates an
expression in the context of the prefetched row for a given (pkey, ckey),
falling back to options-only evaluate() when no selection is available
(non-LWT context) or no column values are needed, and treating absent
columns needed by the expression as null.

Extend constants::setter to use this method:
- setter::execute() now calls evaluate_on_prefetched_row() or evaluate()
  as needed.
- setter::requires_read() returns true when the expression contains a
  column_value node, triggering a prefetch read.
- setter::requires_lwt() mirrors requires_read(), enforcing that column-
  referencing arithmetic is only allowed inside a conditional (IF) statement.

We'll use this new feature to implement "SET r = r + 1" and similar
expressions in the next patch.
2026-05-25 10:09:11 +03:00

85 lines
3.0 KiB
C++

/*
* Copyright (C) 2015-present ScyllaDB
*
* Modified by ScyllaDB
*/
/*
* SPDX-License-Identifier: (LicenseRef-ScyllaDB-Source-Available-1.1 and Apache-2.0)
*/
#include "cql3/constants.hh"
#include "cql3/expr/evaluate.hh"
#include "cql3/expr/expr-utils.hh"
#include "types/concrete_types.hh"
namespace cql3 {
constants::setter::setter(const column_definition& column, expr::expression e)
: operation_skip_if_unset(column, std::move(e))
, _requires_read(_e && expr::find_in_expression<expr::column_value>(*_e, [](const expr::column_value& cv) {
// primary key columns are always available to an update, they don't
// require a read.
return !cv.col->is_primary_key();
}) != nullptr)
{ }
void
constants::setter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
auto value = _requires_read
? params.evaluate_on_prefetched_row(
*_e,
m.key(),
column.is_static() ? clustering_key_prefix::make_empty() : prefix)
: expr::evaluate(*_e, params._options);
execute(m, prefix, params, column, value.view());
}
void
constants::setter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params, const column_definition& column, cql3::raw_value_view value) {
if (value.is_null()) {
m.set_cell(prefix, column, params.make_dead_cell());
} else if (value.is_value()) {
m.set_cell(prefix, column, params.make_cell(*column.type, value));
}
}
void
constants::adder::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
auto value = expr::evaluate(*_e, params._options);
if (value.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value for counter increment");
}
auto increment = value.view().deserialize<int64_t>(*long_type);
m.set_cell(prefix, column, params.make_counter_update_cell(increment));
}
void
constants::subtracter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
auto value = expr::evaluate(*_e, params._options);
if (value.is_null()) {
throw exceptions::invalid_request_exception("Invalid null value for counter increment");
}
auto increment = value.view().deserialize<int64_t>(*long_type);
if (increment == std::numeric_limits<int64_t>::min()) {
throw exceptions::invalid_request_exception(format("The negation of {:d} overflows supported counter precision (signed 8 bytes integer)", increment));
}
m.set_cell(prefix, column, params.make_counter_update_cell(-increment));
}
void constants::deleter::execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) {
if (column.type->is_multi_cell()) {
m.set_cell(prefix, column, collection_mutation_writer(params.make_tombstone()).finish());
} else {
m.set_cell(prefix, column, params.make_dead_cell());
}
}
expr::expression
constants::setter::prepare_new_value_for_broadcast_tables() const {
return *_e;
}
}