cql3: expr: introduce expr::visit, replacing std::visit

The new expr::visit() is just a wrapper around std::visit(),
but has better constraints. A call to expr::visit() with a
visitor that misses an overload will produce an error message
that points at the missing type. This is done using the new
invocable_on_expression concept. Note it lists the expression
types one by one rather than using template magic, since
otherwise we won't get the nice messages.

Later, we will change the implementation when expression becomes
our own type rather than std::variant.

Call sites are updated.
This commit is contained in:
Avi Kivity
2021-09-28 19:53:24 +03:00
parent 2b3ae6aca4
commit e7db3def4f
6 changed files with 53 additions and 29 deletions

View File

@@ -91,7 +91,7 @@ namespace {
using children_t = std::vector<expression>; // conjunction's children.
children_t explode_conjunction(expression e) {
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[] (const conjunction& c) { return std::move(c.children); },
[&] (const auto&) { return children_t{std::move(e)}; },
}, e);
@@ -461,7 +461,7 @@ value_set intersection(value_set a, value_set b, const abstract_type* type) {
}
bool is_satisfied_by(const binary_operator& opr, const column_value_eval_bag& bag) {
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[&] (const column_value& col) {
if (opr.op == oper_t::EQ) {
return equal(*opr.rhs, col, bag);
@@ -541,7 +541,7 @@ 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{
return expr::visit(overloaded_functor{
[] (const constant& constant_val) {
std::optional<bool> bool_val = get_bool_value(constant_val);
if (bool_val.has_value()) {
@@ -686,7 +686,7 @@ 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{
return expr::visit(overloaded_functor{
[] (const constant& constant_val) {
std::optional<bool> bool_val = get_bool_value(constant_val);
if (bool_val.has_value()) {
@@ -704,7 +704,7 @@ value_set possible_lhs_values(const column_definition* cdef, const expression& e
});
},
[&] (const binary_operator& oper) -> value_set {
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[&] (const column_value& col) -> value_set {
if (!cdef || cdef != col.col) {
return unbounded_value_set;
@@ -875,12 +875,12 @@ nonwrapping_range<managed_bytes> to_range(const value_set& s) {
bool is_supported_by(const expression& expr, const secondary_index::index& idx) {
using std::placeholders::_1;
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[&] (const conjunction& conj) {
return boost::algorithm::all_of(conj.children, std::bind(is_supported_by, _1, idx));
},
[&] (const binary_operator& oper) {
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[&] (const column_value& col) {
return idx.supports_expression(*col.col, oper.op);
},
@@ -960,7 +960,7 @@ std::ostream& operator<<(std::ostream& os, const column_value& cv) {
}
std::ostream& operator<<(std::ostream& os, const expression& expr) {
std::visit(overloaded_functor{
expr::visit(overloaded_functor{
[&] (const constant& v) { os << v.value.to_view(); },
[&] (const conjunction& conj) { fmt::print(os, "({})", fmt::join(conj.children, ") AND (")); },
[&] (const binary_operator& opr) {
@@ -1107,7 +1107,7 @@ expression search_and_replace(const expression& e,
if (replace_result) {
return std::move(*replace_result);
} else {
return std::visit(
return expr::visit(
overloaded_functor{
[&] (const conjunction& conj) -> expression {
return conjunction{
@@ -1206,7 +1206,7 @@ std::vector<expression> extract_single_column_restrictions_for_column(const expr
void operator()(const conjunction& conj) {
for (const expression& child : conj.children) {
std::visit(*this, child);
expr::visit(*this, child);
}
}
@@ -1217,7 +1217,7 @@ std::vector<expression> extract_single_column_restrictions_for_column(const expr
}
current_binary_operator = &oper;
std::visit(*this, *oper.lhs);
expr::visit(*this, *oper.lhs);
current_binary_operator = nullptr;
}
@@ -1247,7 +1247,7 @@ std::vector<expression> extract_single_column_restrictions_for_column(const expr
.current_binary_operator = nullptr,
};
std::visit(v, expr);
expr::visit(v, expr);
return std::move(v.restrictions);
}
@@ -1340,7 +1340,7 @@ cql3::raw_value_view evaluate_to_raw_view(term& term_ref, const query_options& o
}
constant evaluate(const expression& e, const query_options& options) {
return std::visit(overloaded_functor {
return expr::visit(overloaded_functor {
[](const binary_operator&) -> constant {
on_internal_error(expr_logger, "Can't evaluate a binary_operator");
},

View File

@@ -98,6 +98,30 @@ using expression = std::variant<conjunction, binary_operator, column_value, toke
template <typename T>
concept ExpressionElement = utils::VariantElement<T, expression>;
template <typename Func>
concept invocable_on_expression
= std::invocable<Func, conjunction>
&& std::invocable<Func, binary_operator>
&& std::invocable<Func, column_value>
&& std::invocable<Func, token>
&& std::invocable<Func, unresolved_identifier>
&& std::invocable<Func, column_mutation_attribute>
&& std::invocable<Func, function_call>
&& std::invocable<Func, cast>
&& std::invocable<Func, field_selection>
&& std::invocable<Func, null>
&& std::invocable<Func, bind_variable>
&& std::invocable<Func, untyped_constant>
&& std::invocable<Func, constant>
&& std::invocable<Func, tuple_constructor>
&& std::invocable<Func, collection_constructor>
&& std::invocable<Func, usertype_constructor>
;
auto visit(invocable_on_expression auto&& visitor, const expression& expr) {
return std::visit(visitor, expr);
}
// An expression that doesn't contain subexpressions
template <typename E>
concept LeafExpression
@@ -345,7 +369,7 @@ extern std::ostream& operator<<(std::ostream&, const expression&);
template<typename Fn>
requires std::regular_invocable<Fn, const binary_operator&>
const binary_operator* find_atom(const expression& e, Fn f) {
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[&] (const binary_operator& op) { return f(op) ? &op : nullptr; },
[] (const constant&) -> const binary_operator* { return nullptr; },
[&] (const conjunction& conj) -> const binary_operator* {
@@ -414,7 +438,7 @@ const binary_operator* find_atom(const expression& e, Fn f) {
template<typename Fn>
requires std::regular_invocable<Fn, const binary_operator&>
size_t count_if(const expression& e, Fn f) {
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[&] (const binary_operator& op) -> size_t { return f(op) ? 1 : 0; },
[&] (const conjunction& conj) {
return std::accumulate(conj.children.cbegin(), conj.children.cend(), size_t{0},

View File

@@ -784,7 +784,7 @@ 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{
return expr::visit(overloaded_functor{
[] (const constant&) -> ::shared_ptr<term> {
on_internal_error(expr_logger, "Can't prepare constant_value, it should not appear in parser output");
},
@@ -849,7 +849,7 @@ prepare_term(const expression& expr, database& db, const sstring& keyspace, lw_s
::shared_ptr<term>
prepare_term_multi_column(const expression& expr, database& db, const sstring& keyspace, const std::vector<lw_shared_ptr<column_specification>>& receivers) {
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[&] (const bind_variable& bv) -> ::shared_ptr<term> {
switch (bv.shape) {
case expr::bind_variable::shape_type::scalar: on_internal_error(expr_logger, "prepare_term_multi_column(bind_variable(scalar))");
@@ -871,7 +871,7 @@ prepare_term_multi_column(const expression& expr, database& db, const sstring& k
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{
return expr::visit(overloaded_functor{
[&] (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()");

View File

@@ -164,7 +164,7 @@ static std::vector<expr::expression> extract_partition_range(
const binary_operator* current_binary_operator = nullptr;
void operator()(const conjunction& c) {
std::ranges::for_each(c.children, [this] (const expression& child) { std::visit(*this, child); });
std::ranges::for_each(c.children, [this] (const expression& child) { expr::visit(*this, child); });
}
void operator()(const binary_operator& b) {
@@ -172,7 +172,7 @@ static std::vector<expr::expression> extract_partition_range(
throw std::logic_error("Nested binary operators are not supported");
}
current_binary_operator = &b;
std::visit(*this, *b.lhs);
expr::visit(*this, *b.lhs);
current_binary_operator = nullptr;
}
@@ -246,7 +246,7 @@ static std::vector<expr::expression> extract_partition_range(
on_internal_error(rlogger, "extract_partition_range(usertype_constructor)");
}
} v;
std::visit(v, where_clause);
expr::visit(v, where_clause);
if (v.tokens) {
return {std::move(*v.tokens)};
}
@@ -273,7 +273,7 @@ static std::vector<expr::expression> extract_clustering_prefix_restrictions(
const binary_operator* current_binary_operator = nullptr;
void operator()(const conjunction& c) {
std::ranges::for_each(c.children, [this] (const expression& child) { std::visit(*this, child); });
std::ranges::for_each(c.children, [this] (const expression& child) { expr::visit(*this, child); });
}
void operator()(const binary_operator& b) {
@@ -281,7 +281,7 @@ static std::vector<expr::expression> extract_clustering_prefix_restrictions(
throw std::logic_error("Nested binary operators are not supported");
}
current_binary_operator = &b;
std::visit(*this, *b.lhs);
expr::visit(*this, *b.lhs);
current_binary_operator = nullptr;
}
@@ -356,7 +356,7 @@ static std::vector<expr::expression> extract_clustering_prefix_restrictions(
on_internal_error(rlogger, "extract_clustering_prefix_restrictions(usertype_constructor)");
}
} v;
std::visit(v, where_clause);
expr::visit(v, where_clause);
if (!v.multi.empty()) {
return move(v.multi);
@@ -996,7 +996,7 @@ struct multi_column_range_accumulator {
}
void operator()(const conjunction& c) {
std::ranges::for_each(c.children, [this] (const expression& child) { std::visit(*this, child); });
std::ranges::for_each(c.children, [this] (const expression& child) { expr::visit(*this, child); });
}
void operator()(const constant& v) {
@@ -1103,7 +1103,7 @@ std::vector<query::clustering_range> get_multi_column_clustering_bounds(
const std::vector<expression>& multi_column_restrictions) {
multi_column_range_accumulator acc{options, schema};
for (const auto& restr : multi_column_restrictions) {
std::visit(acc, restr);
expr::visit(acc, restr);
}
return acc.ranges;
}

View File

@@ -141,7 +141,7 @@ selectable::with_cast::to_string() const {
shared_ptr<selectable>
prepare_selectable(const schema& s, const expr::expression& raw_selectable) {
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[&] (const expr::constant&) -> shared_ptr<selectable> {
on_internal_error(slogger, "no way to express SELECT constant in the grammar yet");
},
@@ -228,7 +228,7 @@ prepare_selectable(const schema& s, const expr::expression& raw_selectable) {
bool
selectable_processes_selection(const expr::expression& raw_selectable) {
return std::visit(overloaded_functor{
return expr::visit(overloaded_functor{
[&] (const expr::constant&) -> bool {
on_internal_error(slogger, "no way to express SELECT constant in the grammar yet");
},

View File

@@ -204,7 +204,7 @@ future<shared_ptr<cql_transport::event::schema_change>> create_view_statement::a
auto& selectable = selector->selectable_;
shared_ptr<column_identifier::raw> identifier;
std::visit(overloaded_functor{
expr::visit(overloaded_functor{
[&] (const expr::unresolved_identifier& ui) { identifier = ui.ident; },
[] (const auto& default_case) -> void { throw exceptions::invalid_request_exception(format("Cannot use general expressions when defining a materialized view")); },
}, selectable);