From 25caa1950daefc2e19068459f2cdb77fc71e18af Mon Sep 17 00:00:00 2001 From: Jan Ciolek Date: Wed, 8 Sep 2021 14:08:05 +0200 Subject: [PATCH] cql3: expr: Add evaluate_IN_list A list representing IN values might contain NULLs before evaluation. We can remove them during evaluation, because nothing equals NULL. If we don't remove them, there are gonna be errors, because a list can't contain NULLs. Signed-off-by: Jan Ciolek --- cql3/expr/expression.cc | 37 +++++++++++++++++++++++++++++++++++++ cql3/expr/expression.hh | 6 ++++++ cql3/lists.cc | 19 +++++++++++++++++++ cql3/lists.hh | 3 +++ 4 files changed, 65 insertions(+) diff --git a/cql3/expr/expression.cc b/cql3/expr/expression.cc index 4cd5535ac1..c660719858 100644 --- a/cql3/expr/expression.cc +++ b/cql3/expr/expression.cc @@ -1364,6 +1364,43 @@ constant evaluate(term& term_ref, const query_options& options) { return evaluate(&term_ref, options); } +constant evaluate_IN_list(term* term_ptr, const query_options& options) { + if (term_ptr == nullptr) { + return constant::make_null(); + } + + ::shared_ptr bound; + lists::delayed_value* delayed_list = dynamic_cast(term_ptr); + if (delayed_list != nullptr) { + bound = delayed_list->bind_ignore_null(options); + } else { + bound = term_ptr->bind(options); + } + + if (bound.get() == nullptr) { + return constant::make_null(); + } + + lists::value* list_value = dynamic_cast(bound.get()); + if (list_value != nullptr) { + // Remove NULL elements from the list + std::remove_if(list_value->_elements.begin(), list_value->_elements.end(), + [](const managed_bytes_opt& element) { return !element.has_value(); }); + } + + raw_value raw_val = bound->get(options); + data_type val_type = bound->get_value_type(); + return constant(std::move(raw_val), std::move(val_type)); +} + +constant evaluate_IN_list(const ::shared_ptr& term_ptr, const query_options& options) { + return evaluate_IN_list(term_ptr.get(), options); +} + +constant evaluate_IN_list(term& term_ref, const query_options& options) { + return evaluate_IN_list(&term_ref, options); +} + cql3::raw_value_view evaluate_to_raw_view(const ::shared_ptr& term_ptr, const query_options& options) { constant value = evaluate(term_ptr, options); return cql3::raw_value_view::make_temporary(std::move(value.value)); diff --git a/cql3/expr/expression.hh b/cql3/expr/expression.hh index 6b69fe4635..ae19e27b9f 100644 --- a/cql3/expr/expression.hh +++ b/cql3/expr/expression.hh @@ -546,6 +546,12 @@ constant evaluate(const ::shared_ptr&, const query_options&); constant evaluate(term*, const query_options&); constant evaluate(term&, const query_options&); +// Similar to evaluate(), but ignores any NULL values in the final list value. +// In an IN restriction nulls can be ignored, because nothing equals NULL. +constant evaluate_IN_list(const ::shared_ptr&, const query_options&); +constant evaluate_IN_list(term*, const query_options&); +constant evaluate_IN_list(term&, const query_options&); + // Calls evaluate() on the term and then converts the constant to raw_value_view cql3::raw_value_view evaluate_to_raw_view(const ::shared_ptr&, const query_options&); cql3::raw_value_view evaluate_to_raw_view(term&, const query_options&); diff --git a/cql3/lists.cc b/cql3/lists.cc index e6b9ff7b02..5b0d6de937 100644 --- a/cql3/lists.cc +++ b/cql3/lists.cc @@ -149,6 +149,25 @@ lists::delayed_value::bind(const query_options& options) { return ::make_shared(buffers, _my_type); } +shared_ptr +lists::delayed_value::bind_ignore_null(const query_options& options) { + utils::chunked_vector buffers; + buffers.reserve(_elements.size()); + for (auto&& t : _elements) { + auto bo = t->bind_and_get(options); + + if (bo.is_null()) { + continue; + } + if (bo.is_unset_value()) { + return constants::UNSET_VALUE; + } + + buffers.push_back(bo.with_value([] (const FragmentedView auto& v) { return managed_bytes(v); })); + } + return ::make_shared(buffers, _my_type); +} + ::shared_ptr lists::marker::bind(const query_options& options) { const auto& value = options.get_value_at(_bind_index); diff --git a/cql3/lists.hh b/cql3/lists.hh index fe27c75624..b5200e9042 100644 --- a/cql3/lists.hh +++ b/cql3/lists.hh @@ -93,6 +93,9 @@ public: virtual bool contains_bind_marker() const override; virtual void fill_prepare_context(prepare_context& ctx) const override; virtual shared_ptr bind(const query_options& options) override; + + // Binds the value, but skips all nulls inside the list + virtual shared_ptr bind_ignore_null(const query_options& options); const std::vector>& get_elements() const { return _elements; }