From c26f2a3d6936d6ff2f14c6ab19b3727c2f86c941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20de=20la=20Pe=C3=B1a?= Date: Mon, 20 Nov 2023 13:42:10 +0000 Subject: [PATCH] Fix SAI unindexed contexts not considering CONTAINS KEY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit patch by Andrés de la Peña; reviewed by Maxwell Guo and Michael Semb Wever for CASSANDRA-19040 --- CHANGES.txt | 1 + .../index/sai/plan/QueryController.java | 4 +- .../index/sai/cql/CollectionIndexingTest.java | 40 ++++++++++++++++--- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d744f50fa1..19c16fde48 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.0-beta1 + * Fix SAI unindexed contexts not considering CONTAINS KEY (CASSANDRA-19040) * Ensure that empty SAI column indexes do not fail on validation after full-SSTable streaming (CASSANDRA-19017) * SAI in-memory index should check max term size (CASSANDRA-18926) * Set default disk_access_mode to mmap_index_only (CASSANDRA-19021) diff --git a/src/java/org/apache/cassandra/index/sai/plan/QueryController.java b/src/java/org/apache/cassandra/index/sai/plan/QueryController.java index eee85f016e..b236c61db4 100644 --- a/src/java/org/apache/cassandra/index/sai/plan/QueryController.java +++ b/src/java/org/apache/cassandra/index/sai/plan/QueryController.java @@ -146,7 +146,9 @@ public class QueryController cfs.getPartitioner(), cfs.getComparator(), expression.column(), - IndexTarget.Type.VALUES, + expression.operator().isContainsKey() + ? IndexTarget.Type.KEYS + : IndexTarget.Type.VALUES, null) : indexes.iterator().next().getIndexContext(); } diff --git a/test/unit/org/apache/cassandra/index/sai/cql/CollectionIndexingTest.java b/test/unit/org/apache/cassandra/index/sai/cql/CollectionIndexingTest.java index a82f539842..b490dfa946 100644 --- a/test/unit/org/apache/cassandra/index/sai/cql/CollectionIndexingTest.java +++ b/test/unit/org/apache/cassandra/index/sai/cql/CollectionIndexingTest.java @@ -42,28 +42,28 @@ public class CollectionIndexingTest extends SAITester } @Test - public void indexMap() throws Throwable + public void indexMap() { createPopulatedMap(createIndexDDL("value")); assertEquals(2, execute("SELECT * FROM %s WHERE value CONTAINS 'v1'").size()); } @Test - public void indexMapKeys() throws Throwable + public void indexMapKeys() { createPopulatedMap(createIndexDDL("KEYS(value)")); assertEquals(2, execute("SELECT * FROM %s WHERE value CONTAINS KEY 1").size()); } @Test - public void indexMapValues() throws Throwable + public void indexMapValues() { createPopulatedMap(createIndexDDL("VALUES(value)")); assertEquals(2, execute("SELECT * FROM %s WHERE value CONTAINS 'v1'").size()); } @Test - public void indexMapEntries() throws Throwable + public void indexMapEntries() { createPopulatedMap(createIndexDDL("ENTRIES(value)")); assertEquals(2, execute("SELECT * FROM %s WHERE value[1] = 'v1'").size()); @@ -71,7 +71,7 @@ public class CollectionIndexingTest extends SAITester } @Test - public void indexFrozenList() throws Throwable + public void indexFrozenList() { createPopulatedFrozenList(createIndexDDL("FULL(value)")); assertEquals(2, execute("SELECT * FROM %s WHERE value = ?", Arrays.asList(1, 2, 3)).size()); @@ -176,6 +176,34 @@ public class CollectionIndexingTest extends SAITester assertUnsupportedIndexOperator(2, "SELECT * FROM %s WHERE value[1] = 'v1'"); } + @Test + public void unindexedContainsExpressions() + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, v int, m map)"); + createIndex("CREATE INDEX ON %s(v) USING 'SAI'"); // just to make sure that SAI is involved + + Object[] row = row(0, 1, map(2, 3)); + execute("INSERT INTO %s (k, v, m) VALUES (?, ?, ?)", row); + execute("INSERT INTO %s (k, v, m) VALUES (?, ?, ?)", 1, 1, map(12, 13)); + + // try without any indexes on the map + assertRows(execute("SELECT k, v, m FROM %s WHERE v = 1 AND m CONTAINS 3 ALLOW FILTERING"), row); + assertRows(execute("SELECT k, v, m FROM %s WHERE v = 1 AND m CONTAINS KEY 2 ALLOW FILTERING"), row); + assertRows(execute("SELECT k, v, m FROM %s WHERE v = 1 AND m CONTAINS KEY 2 AND m CONTAINS 3 ALLOW FILTERING"), row); + + // try with index on map values + createIndex("CREATE INDEX ON %s(m) USING 'SAI'"); + assertRows(execute("SELECT k, v, m FROM %s WHERE v = 1 AND m CONTAINS 3"), row); + assertRows(execute("SELECT k, v, m FROM %s WHERE v = 1 AND m CONTAINS KEY 2 ALLOW FILTERING"), row); + assertRows(execute("SELECT k, v, m FROM %s WHERE v = 1 AND m CONTAINS KEY 2 AND m CONTAINS 3 ALLOW FILTERING"), row); + + // try with index on map keys + createIndex("CREATE INDEX ON %s(KEYS(m)) USING 'SAI'"); + assertRows(execute("SELECT k, v, m FROM %s WHERE v = 1 AND m CONTAINS 3"), row); + assertRows(execute("SELECT k, v, m FROM %s WHERE v = 1 AND m CONTAINS KEY 2"), row); + assertRows(execute("SELECT k, v, m FROM %s WHERE v = 1 AND m CONTAINS KEY 2 AND m CONTAINS 3"), row); + } + private void createPopulatedMap(String createIndex) { createTable("CREATE TABLE %s (pk int primary key, value map)"); @@ -191,7 +219,7 @@ public class CollectionIndexingTest extends SAITester } @SuppressWarnings("SameParameterValue") - private void createPopulatedFrozenMap(String createIndex) throws Throwable + private void createPopulatedFrozenMap(String createIndex) { createTable("CREATE TABLE %s (pk int primary key, value frozen>)"); createIndex(createIndex);