diff --git a/cql3/selection/selection.cc b/cql3/selection/selection.cc index 5ee9556cc6..b7c2297085 100644 --- a/cql3/selection/selection.cc +++ b/cql3/selection/selection.cc @@ -450,11 +450,16 @@ bool result_set_builder::restrictions_filter::do_filter(const selection& selecti } auto clustering_columns_restrictions = _restrictions->get_clustering_columns_restrictions(); - if (dynamic_pointer_cast(clustering_columns_restrictions)) { + bool has_multi_col_clustering_restrictions = + dynamic_pointer_cast(clustering_columns_restrictions) != nullptr; + if (has_multi_col_clustering_restrictions) { clustering_key_prefix ckey = clustering_key_prefix::from_exploded(clustering_key); - return expr::is_satisfied_by( + bool multi_col_clustering_satisfied = expr::is_satisfied_by( clustering_columns_restrictions->expression, partition_key, clustering_key, static_row, row, selection, _options); + if (!multi_col_clustering_satisfied) { + return false; + } } auto static_row_iterator = static_row.iterator(); @@ -502,6 +507,13 @@ bool result_set_builder::restrictions_filter::do_filter(const selection& selecti if (_skip_ck_restrictions) { continue; } + if (has_multi_col_clustering_restrictions) { + // Mixing multi column and single column restrictions on clustering + // key columns is forbidden. + // Since there are multi column restrictions we have to skip + // evaluating single column restrictions or we will get an error. + continue; + } auto clustering_key_restrictions_map = _restrictions->get_single_column_clustering_key_restrictions(); auto restr_it = clustering_key_restrictions_map.find(cdef); if (restr_it == clustering_key_restrictions_map.end()) { diff --git a/test/boost/restrictions_test.cc b/test/boost/restrictions_test.cc index 31ca790016..8f83ff5684 100644 --- a/test/boost/restrictions_test.cc +++ b/test/boost/restrictions_test.cc @@ -763,9 +763,8 @@ SEASTAR_THREAD_TEST_CASE(multi_col_in) { cquery_nofail(e, "insert into t(pk,ck1,ck2,r) values (4,13,23,'a')"); require_rows(e, "select pk from t where (ck1,ck2) in ((13,23)) allow filtering", {{I(3)}, {I(4)}}); require_rows(e, "select pk from t where (ck1) in ((13),(33),(44)) allow filtering", {{I(3)}, {I(4)}}); - // TODO: uncomment when #6200 is fixed. - // require_rows(e, "select pk from t where (ck1,ck2) in ((13,23)) and r='a' allow filtering", - // {{I(4), I(13), F(23), T("a")}}); + require_rows(e, "select pk from t where (ck1,ck2) in ((13,23)) and r='a' allow filtering", + {{I(4), I(13), F(23), T("a")}}); cquery_nofail(e, "delete from t where pk=4"); require_rows(e, "select pk from t where (ck1,ck2) in ((13,23)) allow filtering", {{I(3)}}); auto stmt = e.prepare("select ck1 from t where (ck1,ck2) in ? allow filtering").get0(); diff --git a/test/cql-pytest/test_scan.py b/test/cql-pytest/test_scan.py index 6900aaecfc..e24e2f1dfa 100644 --- a/test/cql-pytest/test_scan.py +++ b/test/cql-pytest/test_scan.py @@ -27,3 +27,38 @@ def test_scan_ending_with_static_row(cql, test_keyspace): # results. The success criteria for this test is the query finishing # without errors. res = list(cql.execute(statement)) + + +# Test that if we have multi-column restrictions on the clustering key +# and additional filtering on regular columns, both restrictions are obeyed. +# Reproduces #6200. +def test_multi_column_restrictions_and_filtering(cql, test_keyspace): + with new_test_table(cql, test_keyspace, "p int, c1 int, c2 int, r int, PRIMARY KEY (p, c1, c2)") as table: + stmt = cql.prepare(f"INSERT INTO {table} (p, c1, c2, r) VALUES (1, ?, ?, ?)") + for i in range(2): + for j in range(2): + cql.execute(stmt, [i, j, j]) + assert list(cql.execute(f"SELECT c1,c2,r FROM {table} WHERE p=1 AND (c1, c2) = (0,1)")) == [(0,1,1)] + # Since in that result r=1, adding "AND r=1" should return the same + # result, and adding "AND r=0" should return nothing. + assert list(cql.execute(f"SELECT c1,c2,r FROM {table} WHERE p=1 AND (c1, c2) = (0,1) AND r=1 ALLOW FILTERING")) == [(0,1,1)] + # Reproduces #6200: + assert list(cql.execute(f"SELECT c1,c2,r FROM {table} WHERE p=1 AND (c1, c2) = (0,1) AND r=0 ALLOW FILTERING")) == [] + +# Test that if we have a range multi-column restrictions on the clustering key +# and additional filtering on regular columns, both restrictions are obeyed. +# Similar to test_multi_column_restrictions_and_filtering, but uses a range +# restriction on the clustering key columns. +# Reproduces #12014, the code is taken from a reproducer provided by a user. +def test_multi_column_range_restrictions_and_filtering(cql, test_keyspace): + with new_test_table(cql, test_keyspace, "pk int, ts timestamp, id int, processed boolean, PRIMARY KEY (pk, ts, id)") as table: + cql.execute(f"INSERT INTO {table} (pk, ts, id, processed) VALUES (0, currentTimestamp(), 0, true)") + cql.execute(f"INSERT INTO {table} (pk, ts, id, processed) VALUES (0, currentTimestamp(), 1, true)") + cql.execute(f"INSERT INTO {table} (pk, ts, id, processed) VALUES (0, currentTimestamp(), 2, false)") + cql.execute(f"INSERT INTO {table} (pk, ts, id, processed) VALUES (0, currentTimestamp(), 3, false)") + # This select doesn't use multi-column restrictions, the result shouldn't change when it does. + rows1 = list(cql.execute(f"SELECT id, processed FROM {table} WHERE pk = 0 AND ts >= 0 AND processed = false ALLOW FILTERING")) + assert rows1 == [(2, False), (3, False)] + # Reproduces #12014 + rows2 = list(cql.execute(f"SELECT id, processed FROM {table} WHERE pk = 0 AND (ts, id) >= (0, 0) AND processed = false ALLOW FILTERING")) + assert rows1 == rows2