cql3: allow lists of IN elements to be NULL

Requests like `col IN NULL` used to cause
an error - Invalid null value for colum col.

We would like to allow NULLs everywhere.
When a NULL occurs on either side
of a binary operator, the whole operation
should just evaluate to NULL.

Signed-off-by: Jan Ciolek <jan.ciolek@scylladb.com>

Closes #11775
This commit is contained in:
Jan Ciolek
2022-10-12 13:09:46 +02:00
committed by Kamil Braun
parent 19e62d4704
commit 52bbc1065c
4 changed files with 17 additions and 16 deletions

View File

@@ -460,8 +460,9 @@ bool like(const column_value& cv, const raw_value_view& pattern, const evaluatio
/// True iff the column value is in the set defined by rhs.
bool is_one_of(const expression& col, const expression& rhs, const evaluation_inputs& inputs) {
const cql3::raw_value in_list = evaluate(rhs, inputs);
statements::request_validations::check_false(
in_list.is_null(), "Invalid null value for column {}", col);
if (in_list.is_null()) {
return false;
}
return boost::algorithm::any_of(get_list_elements(in_list), [&] (const managed_bytes_opt& b) {
return equal(col, b, inputs);
@@ -697,7 +698,9 @@ value_list get_IN_values(
if (in_list.is_unset_value()) {
throw exceptions::invalid_request_exception(format("Invalid unset value for column {}", column_name));
}
statements::request_validations::check_false(in_list.is_null(), "Invalid null value for column {}", column_name);
if (in_list.is_null()) {
return value_list();
}
utils::chunked_vector<managed_bytes> list_elems = get_list_elements(in_list);
return to_sorted_vector(std::move(list_elems) | non_null | deref, comparator);
}

View File

@@ -1169,8 +1169,11 @@ struct multi_column_range_accumulator {
intersect_all(to_range(binop.op, clustering_key_prefix(std::move(values))));
} else if (binop.op == oper_t::IN) {
const cql3::raw_value tup = expr::evaluate(binop.rhs, options);
statements::request_validations::check_false(tup.is_null(), "Invalid null value for IN restriction");
process_in_values(expr::get_list_of_tuples_elements(tup, *type_of(binop.rhs)));
utils::chunked_vector<std::vector<managed_bytes_opt>> tuple_elems;
if (tup.is_value()) {
tuple_elems = expr::get_list_of_tuples_elements(tup, *type_of(binop.rhs));
}
process_in_values(std::move(tuple_elems));
} else {
on_internal_error(rlogger, format("multi_column_range_accumulator: unexpected atom {}", binop));
}

View File

@@ -89,8 +89,7 @@ def testClusteringColumnRelations(cql, test_keyspace):
["first", 2, 6, 2],
["first", 3, 7, 3])
assert_invalid_message(cql, table, "Invalid null value for column b",
"select * from %s where a = ? and b in ? and c in ?", "first", None, [7, 6])
assert_empty(execute(cql, table, "select * from %s where a = ? and b in ? and c in ?", "first", None, [7, 6]))
assert_rows(execute(cql, table, "select * from %s where a = ? and c >= ? and b in (?, ?)", "first", 6, 3, 2),
["first", 2, 6, 2],

View File

@@ -52,21 +52,17 @@ def test_insert_null_key(cql, table1):
# Tests handling of "key_column in ?" where ? is bound to null.
# Reproduces issue #8265.
def test_primary_key_in_null(cql, table1):
with pytest.raises(InvalidRequest, match='null value'):
cql.execute(cql.prepare(f"SELECT p FROM {table1} WHERE p IN ?"), [None])
with pytest.raises(InvalidRequest, match='null value'):
cql.execute(cql.prepare(f"SELECT p FROM {table1} WHERE p='' AND c IN ?"), [None])
with pytest.raises(InvalidRequest, match='Invalid null value for IN restriction'):
cql.execute(cql.prepare(f"SELECT p FROM {table1} WHERE p='' AND (c) IN ?"), [None])
def test_primary_key_in_null(scylla_only, cql, table1):
assert list(cql.execute(cql.prepare(f"SELECT p FROM {table1} WHERE p IN ?"), [None])) == []
assert list(cql.execute(cql.prepare(f"SELECT p FROM {table1} WHERE p='' AND c IN ?"), [None])) == []
assert list(cql.execute(cql.prepare(f"SELECT p FROM {table1} WHERE p='' AND (c) IN ?"), [None])) == []
# Cassandra says "IN predicates on non-primary-key columns (v) is not yet supported".
def test_regular_column_in_null(scylla_only, cql, table1):
'''Tests handling of "regular_column in ?" where ? is bound to null.'''
# Without any rows in the table, SELECT will shortcircuit before evaluating the WHERE clause.
cql.execute(f"INSERT INTO {table1} (p,c) VALUES ('p', 'c')")
with pytest.raises(InvalidRequest, match='null value'):
cql.execute(cql.prepare(f"SELECT v FROM {table1} WHERE v IN ? ALLOW FILTERING"), [None])
assert list(cql.execute(cql.prepare(f"SELECT v FROM {table1} WHERE v IN ? ALLOW FILTERING"), [None])) == []
# Though nonsensical, this operation is allowed by Cassandra. Ensure we allow it, too.
def test_delete_impossible_clustering_range(cql, table1):