cql3: expr: Add constant to expression

Adds constant to the expression variant:
struct constant {
    raw_value value;
    data_type type;
};

This struct will be used to represent constant values with known bytes and type.
This corresponds to the terminal from current design.

bool is removed from expression, now constant is used instead.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>
This commit is contained in:
Jan Ciolek
2021-08-18 11:14:43 +02:00
parent dd9d6c081e
commit ad3d2ee47d
7 changed files with 122 additions and 28 deletions

View File

@@ -512,7 +512,7 @@ bool is_satisfied_by(const binary_operator& opr, const column_value_eval_bag& ba
// token range. It is impossible for any fetched row not to match now.
return true;
},
[] (bool) -> bool {
[] (const constant&) -> bool {
on_internal_error(expr_logger, "is_satisfied_by: A constant cannot serve as the LHS of a binary expression");
},
[] (const conjunction&) -> bool {
@@ -556,7 +556,15 @@ bool is_satisfied_by(const binary_operator& opr, const column_value_eval_bag& ba
bool is_satisfied_by(const expression& restr, const column_value_eval_bag& bag) {
return std::visit(overloaded_functor{
[&] (bool v) { return v; },
[] (const constant& constant_val) {
std::optional<bool> bool_val = get_bool_value(constant_val);
if (bool_val.has_value()) {
return *bool_val;
}
on_internal_error(expr_logger,
"is_satisfied_by: a constant that is not a bool value cannot serve as a restriction by itself");
},
[&] (const conjunction& conj) {
return boost::algorithm::all_of(conj.children, [&] (const expression& c) {
return is_satisfied_by(c, bag);
@@ -717,8 +725,14 @@ nonwrapping_range<clustering_key_prefix> to_range(oper_t op, const clustering_ke
value_set possible_lhs_values(const column_definition* cdef, const expression& expr, const query_options& options) {
const auto type = cdef ? get_value_comparator(cdef) : long_type.get();
return std::visit(overloaded_functor{
[] (bool b) {
return b ? unbounded_value_set : empty_value_set;
[] (const constant& constant_val) {
std::optional<bool> bool_val = get_bool_value(constant_val);
if (bool_val.has_value()) {
return *bool_val ? unbounded_value_set : empty_value_set;
}
on_internal_error(expr_logger,
"possible_lhs_values: a constant that is not a bool value cannot serve as a restriction by itself");
},
[&] (const conjunction& conj) {
return boost::accumulate(conj.children, unbounded_value_set,
@@ -808,7 +822,7 @@ value_set possible_lhs_values(const column_definition* cdef, const expression& e
[&] (const conjunction&) -> value_set {
on_internal_error(expr_logger, "possible_lhs_values: conjunctions are not supported as the LHS of a binary expression");
},
[&] (bool) -> value_set {
[] (const constant&) -> value_set {
on_internal_error(expr_logger, "possible_lhs_values: constants are not supported as the LHS of a binary expression");
},
[] (const unresolved_identifier&) -> value_set {
@@ -924,7 +938,7 @@ bool is_supported_by(const expression& expr, const secondary_index::index& idx)
[&] (const conjunction&) -> bool {
on_internal_error(expr_logger, "is_supported_by: conjunctions are not supported as the LHS of a binary expression");
},
[&] (bool) -> bool {
[] (const constant&) -> bool {
on_internal_error(expr_logger, "is_supported_by: constants are not supported as the LHS of a binary expression");
},
[] (const unresolved_identifier&) -> bool {
@@ -985,7 +999,7 @@ std::ostream& operator<<(std::ostream& os, const column_value& cv) {
std::ostream& operator<<(std::ostream& os, const expression& expr) {
std::visit(overloaded_functor{
[&] (bool b) { os << (b ? "TRUE" : "FALSE"); },
[&] (const constant& v) { os << v.value.to_view(); },
[&] (const conjunction& conj) { fmt::print(os, "({})", fmt::join(conj.children, ") AND (")); },
[&] (const binary_operator& opr) {
os << "(" << *opr.lhs << ") " << opr.op << ' ' << *opr.rhs;
@@ -1224,7 +1238,7 @@ std::vector<expression> extract_single_column_restrictions_for_column(const expr
const column_definition& column;
const binary_operator* current_binary_operator;
void operator()(bool) {}
void operator()(const constant&) {}
void operator()(const conjunction& conj) {
for (const expression& child : conj.children) {
@@ -1275,5 +1289,56 @@ std::vector<expression> extract_single_column_restrictions_for_column(const expr
}
constant::constant(cql3::raw_value val, data_type typ)
: value(std::move(val)), type(std::move(typ)) {
}
constant constant::make_null(data_type val_type) {
return constant(cql3::raw_value::make_null(), std::move(val_type));
}
constant constant::make_unset_value(data_type val_type) {
return constant(cql3::raw_value::make_unset_value(), std::move(val_type));
}
constant constant::make_bool(bool bool_val) {
return constant(raw_value::make_value(boolean_type->decompose(bool_val)), boolean_type);
}
bool constant::is_null() const {
return value.is_null();
}
bool constant::is_unset_value() const {
return value.is_unset_value();
}
bool constant::has_empty_value_bytes() const {
if (is_null_or_unset()) {
return false;
}
return value.to_view().size_bytes() == 0;
}
bool constant::is_null_or_unset() const {
return is_null() || is_unset_value();
}
std::optional<bool> get_bool_value(const constant& constant_val) {
if (constant_val.type->get_kind() != abstract_type::kind::boolean) {
return std::nullopt;
}
if (constant_val.is_null_or_unset()) {
return std::nullopt;
}
if (constant_val.has_empty_value_bytes()) {
return std::nullopt;
}
return constant_val.value.to_view().deserialize<bool>(*boolean_type);
}
} // namespace expr
} // namespace cql3

View File

@@ -83,14 +83,15 @@ class field_selection;
struct null;
struct bind_variable;
struct untyped_constant;
struct constant;
struct tuple_constructor;
struct collection_constructor;
struct usertype_constructor;
/// A CQL expression -- union of all possible expression types. bool means a Boolean constant.
using expression = std::variant<bool, conjunction, binary_operator, column_value, token,
using expression = std::variant<conjunction, binary_operator, column_value, token,
unresolved_identifier, column_mutation_attribute, function_call, cast,
field_selection, null, bind_variable, untyped_constant,
field_selection, null, bind_variable, untyped_constant, constant,
tuple_constructor, collection_constructor, usertype_constructor>;
template <typename T>
@@ -106,6 +107,7 @@ concept LeafExpression
|| std::same_as<null, E>
|| std::same_as<bind_variable, E>
|| std::same_as<untyped_constant, E>
|| std::same_as<constant, E>
;
// An expression variant element can't contain an expression by value, since the size of the variant
@@ -215,6 +217,26 @@ struct untyped_constant {
sstring raw_text;
};
// Represents a constant value with known value and type
// For null and unset the type can sometimes be set to empty_type
struct constant {
// A value serialized using the internal (latest) cql_serialization_format
cql3::raw_value value;
// Never nullptr, for NULL and UNSET might be empty_type
data_type type;
constant(cql3::raw_value value, data_type type);
static constant make_null(data_type val_type = empty_type);
static constant make_unset_value(data_type val_type = empty_type);
static constant make_bool(bool bool_val);
bool is_null() const;
bool is_unset_value() const;
bool is_null_or_unset() const;
bool has_empty_value_bytes() const;
};
// Denotes construction of a tuple from its elements, e.g. ('a', ?, some_column) in CQL.
struct tuple_constructor {
std::vector<expression> elements;
@@ -292,7 +314,7 @@ requires std::regular_invocable<Fn, const binary_operator&>
const binary_operator* find_atom(const expression& e, Fn f) {
return std::visit(overloaded_functor{
[&] (const binary_operator& op) { return f(op) ? &op : nullptr; },
[] (bool) -> const binary_operator* { return nullptr; },
[] (const constant&) -> const binary_operator* { return nullptr; },
[&] (const conjunction& conj) -> const binary_operator* {
for (auto& child : conj.children) {
if (auto found = find_atom(child, f)) {
@@ -365,7 +387,7 @@ size_t count_if(const expression& e, Fn f) {
return std::accumulate(conj.children.cbegin(), conj.children.cend(), size_t{0},
[&] (size_t acc, const expression& c) { return acc + count_if(c, f); });
},
[] (bool) -> size_t { return 0; },
[] (const constant&) -> size_t { return 0; },
[] (const column_value&) -> size_t { return 0; },
[] (const token&) -> size_t { return 0; },
[] (const unresolved_identifier&) -> size_t { return 0; },
@@ -516,6 +538,7 @@ nested_expression::nested_expression(ExpressionElement auto e)
// For example "WHERE c = 1 AND (a, c) = (2, 1) AND token(p) < 2 AND FALSE" will return {"c = 1"}.
std::vector<expression> extract_single_column_restrictions_for_column(const expression&, const column_definition&);
std::optional<bool> get_bool_value(const constant&);
} // namespace expr
} // namespace cql3

View File

@@ -787,8 +787,8 @@ cast_prepare_term(const cast& c, database& db, const sstring& keyspace, lw_share
::shared_ptr<term>
prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) {
return std::visit(overloaded_functor{
[&] (bool bool_constant) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "bool constants are not yet reachable via term_raw_expr::prepare()");
[] (const constant&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "Can't prepare constant_value, it should not appear in parser output");
},
[&] (const binary_operator&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "binary_operators are not yet reachable via term_raw_expr::prepare()");
@@ -874,8 +874,9 @@ assignment_testable::test_result
test_assignment(const expression& expr, database& db, const sstring& keyspace, const column_specification& receiver) {
using test_result = assignment_testable::test_result;
return std::visit(overloaded_functor{
[&] (bool bool_constant) -> test_result {
on_internal_error(expr_logger, "bool constants are not yet reachable via term_raw_expr::test_assignment()");
[&] (const constant&) -> test_result {
// constants shouldn't appear in parser output, only untyped_constants
on_internal_error(expr_logger, "constants are not yet reachable via term_raw_expr::test_assignment()");
},
[&] (const binary_operator&) -> test_result {
on_internal_error(expr_logger, "binary_operators are not yet reachable via term_raw_expr::test_assignment()");

View File

@@ -54,7 +54,7 @@ namespace restrictions {
class restriction {
public:
// Init to false for now, to easily detect errors. This whole class is going away.
cql3::expr::expression expression = false;
cql3::expr::expression expression = expr::constant::make_bool(false);
virtual ~restriction() {}
};

View File

@@ -61,7 +61,7 @@ class statement_restrictions::initial_key_restrictions : public primary_key_rest
public:
initial_key_restrictions(bool allow_filtering)
: _allow_filtering(allow_filtering) {
this->expression = true;
this->expression = expr::constant::make_bool(true);
}
::shared_ptr<primary_key_restrictions<T>> do_merge_to(schema_ptr schema, ::shared_ptr<restriction> restriction) const {
@@ -204,7 +204,7 @@ static std::vector<expr::expression> extract_partition_range(
// Partition key columns are not legal in tuples, so ignore tuples.
}
void operator()(bool) {}
void operator()(const constant&) {}
void operator()(const unresolved_identifier&) {
on_internal_error(rlogger, "extract_partition_range(unresolved_identifier)");
@@ -314,7 +314,7 @@ static std::vector<expr::expression> extract_clustering_prefix_restrictions(
// A token cannot be a clustering prefix restriction
}
void operator()(bool) {}
void operator()(const constant&) {}
void operator()(const unresolved_identifier&) {
on_internal_error(rlogger, "extract_clustering_prefix_restrictions(unresolved_identifier)");
@@ -1011,8 +1011,13 @@ struct multi_column_range_accumulator {
std::ranges::for_each(c.children, [this] (const expression& child) { std::visit(*this, child); });
}
void operator()(bool b) {
if (!b) {
void operator()(const constant& v) {
std::optional<bool> bool_val = get_bool_value(v);
if (!bool_val.has_value()) {
on_internal_error(rlogger, "non-bool constant encountered outside binary operator");
}
if (*bool_val == false) {
ranges.clear();
}
}

View File

@@ -142,8 +142,8 @@ selectable::with_cast::to_string() const {
shared_ptr<selectable>
prepare_selectable(const schema& s, const expr::expression& raw_selectable) {
return std::visit(overloaded_functor{
[&] (bool bool_constant) -> shared_ptr<selectable> {
on_internal_error(slogger, "no way to express SELECT TRUE/FALSE in the grammar yet");
[&] (const expr::constant&) -> shared_ptr<selectable> {
on_internal_error(slogger, "no way to express SELECT constant in the grammar yet");
},
[&] (const expr::conjunction& conj) -> shared_ptr<selectable> {
on_internal_error(slogger, "no way to express 'SELECT a AND b' in the grammar yet");
@@ -229,8 +229,8 @@ prepare_selectable(const schema& s, const expr::expression& raw_selectable) {
bool
selectable_processes_selection(const expr::expression& raw_selectable) {
return std::visit(overloaded_functor{
[&] (bool bool_constant) -> bool {
on_internal_error(slogger, "no way to express SELECT TRUE/FALSE in the grammar yet");
[&] (const expr::constant&) -> bool {
on_internal_error(slogger, "no way to express SELECT constant in the grammar yet");
},
[&] (const expr::conjunction& conj) -> bool {
on_internal_error(slogger, "no way to express 'SELECT a AND b' in the grammar yet");

View File

@@ -452,8 +452,8 @@ BOOST_AUTO_TEST_CASE(expression_extract_column_restrictions) {
expression token_lt_restriction = binary_operator(token{}, oper_t::LT, zero_value);
expression token_gt_restriction = binary_operator(token{}, oper_t::GT, zero_value);
expression true_restriction(true);
expression false_restriction(false);
expression true_restriction = constant::make_bool(true);
expression false_restriction = constant::make_bool(false);
expression token_expr = token{};
expression pk1_expr = column_value(&col_pk1);
expression pk2_expr = column_value(&col_pk1);