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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user