diff --git a/CHANGES.txt b/CHANGES.txt index 309667ebf1..8726a4523c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,6 +9,7 @@ Merged from 3.0: Merged from 2.2: * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973) Merged from 2.1: + * Fix filtering on clustering columns when 2i is used (CASSANDRA-11907) * Avoid stalling paxos when the paxos state expires (CASSANDRA-12043) * Remove finished incoming streaming connections from MessagingService (CASSANDRA-11854) diff --git a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java index 837ee13aa0..dc349d9b7e 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java @@ -175,18 +175,13 @@ final class ClusteringColumnRestrictions extends RestrictionSetWrapper public final boolean needFiltering() { int position = 0; - SingleRestriction slice = null; + for (SingleRestriction restriction : restrictions) { if (handleInFilter(restriction, position)) return true; - if (slice != null && !slice.getFirstColumn().equals(restriction.getFirstColumn())) - return true; - - if (slice == null && restriction.isSlice()) - slice = restriction; - else + if (!restriction.isSlice()) position = restriction.getLastColumn().position() + 1; } return hasContains(); @@ -199,7 +194,6 @@ final class ClusteringColumnRestrictions extends RestrictionSetWrapper { int position = 0; - SingleRestriction slice = null; for (SingleRestriction restriction : restrictions) { // We ignore all the clustering columns that can be handled by slices. @@ -209,15 +203,7 @@ final class ClusteringColumnRestrictions extends RestrictionSetWrapper continue; } - if (slice != null && !slice.getFirstColumn().equals(restriction.getFirstColumn())) - { - restriction.addRowFilterTo(filter, indexManager, options); - continue; - } - - if (slice == null && restriction.isSlice()) - slice = restriction; - else + if (!restriction.isSlice()) position = restriction.getLastColumn().position() + 1; } } diff --git a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java index 012b319242..e5e3bc8ae3 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java @@ -476,7 +476,7 @@ public abstract class MultiColumnRestriction implements SingleRestriction SecondaryIndexManager indexManager, QueryOptions options) { - throw invalidRequest("Slice restrictions are not supported on indexed columns"); + throw invalidRequest("Multi-column slice restrictions cannot be used for filtering."); } @Override diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java index 1239b7a2ff..7f43c6b5b2 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java @@ -897,6 +897,33 @@ public class SelectMultiColumnRelationTest extends CQLTester "SELECT * FROM %s WHERE (b, c) = (?, ?) AND d = ? ALLOW FILTERING", TOO_BIG, 1, 2); } + @Test + public void testMultiColumnRestrictionsWithIndex() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, v int, PRIMARY KEY (a, b, c, d, e))"); + createIndex("CREATE INDEX ON %s (v)"); + for (int i = 1; i <= 5; i++) + { + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, 0, 0, 0, 0); + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, i, 0, 0, 0); + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, i, i, 0, 0); + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, i, i, i, 0); + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, i, i, i, i); + } + + String errorMsg = "Multi-column slice restrictions cannot be used for filtering."; + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND (c,d) < (2,2) AND v = 0 ALLOW FILTERING"); + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND (d,e) < (2,2) AND b = 1 AND v = 0 ALLOW FILTERING"); + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND b = 1 AND (d,e) < (2,2) AND v = 0 ALLOW FILTERING"); + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND b > 1 AND (d,e) < (2,2) AND v = 0 ALLOW FILTERING"); + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND (b,c) > (1,0) AND (d,e) < (2,2) AND v = 0 ALLOW FILTERING"); + } + @Test public void testMultiplePartitionKeyAndMultiClusteringWithIndex() throws Throwable { diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java index dde87d82ce..9a1493b37d 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java @@ -2937,4 +2937,58 @@ public class SelectTest extends CQLTester row("a", 3, 5)); } } + + @Test + public void testFilteringWithSecondaryIndex() throws Throwable + { + createTable("CREATE TABLE %s (pk int, " + + "c1 int, " + + "c2 int, " + + "c3 int, " + + "v int, " + + "PRIMARY KEY (pk, c1, c2, c3))"); + createIndex("CREATE INDEX v_idx_1 ON %s (v);"); + + for (int i = 1; i <= 5; i++) + { + execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 1, 1, i); + execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 1, i, i); + execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, ?)", 1, 1, i, i, i); + execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, ?)", 1, i, i, i, i); + } + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 > 0 AND c1 < 5 AND c2 = 1 AND v = 3 ALLOW FILTERING;"), + row(1, 1, 1, 3, 3)); + + assertEmpty(execute("SELECT * FROM %s WHERE pk = 1 AND c1 > 1 AND c1 < 5 AND c2 = 1 AND v = 3 ALLOW FILTERING;")); + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 > 1 AND c2 > 2 AND c3 > 2 AND v = 3 ALLOW FILTERING;"), + row(1, 3, 3, 3, 3)); + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 > 1 AND c2 > 2 AND c3 = 3 AND v = 3 ALLOW FILTERING;"), + row(1, 3, 3, 3, 3)); + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 IN(0,1,2) AND c2 = 1 AND v = 3 ALLOW FILTERING;"), + row(1, 1, 1, 3, 3)); + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 IN(0,1,2) AND c2 = 1 AND v = 3"), + row(1, 1, 1, 3, 3)); + } + + @Test + public void testIndexQueryWithCompositePartitionKey() throws Throwable + { + createTable("CREATE TABLE %s (p1 int, p2 int, v int, PRIMARY KEY ((p1, p2)))"); + assertInvalidMessage("Partition key parts: p2 must be restricted as other parts are", + "SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING"); + createIndex("CREATE INDEX ON %s(v)"); + + execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 1, 3); + execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 2, 3); + execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 2, 1, 3); + + assertRows(execute("SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING"), + row(1, 2, 3), + row(1, 1, 3)); + } }