This is an automated email from the ASF dual-hosted git repository.

ifesdjeen pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 9bc8d0b  Avoid executing commands that have guaranteed empty bounds in 
slices filter
9bc8d0b is described below

commit 9bc8d0b452aeb7aaa2005e710fc3de0998172738
Author: Alex Petrov <[email protected]>
AuthorDate: Wed Feb 10 15:39:41 2021 +0100

    Avoid executing commands that have guaranteed empty bounds in slices filter
    
    Patch by Alex Petrov; reviewed by Marcus Eriksson and Aleksey Yeschenko for 
CASSANDRA-16435.
---
 .../cassandra/cql3/statements/SelectStatement.java |  2 +-
 .../cassandra/db/SinglePartitionReadCommand.java   |  3 +++
 src/java/org/apache/cassandra/db/Slice.java        | 23 ++++++++++++++-----
 .../db/filter/AbstractClusteringIndexFilter.java   |  5 +++++
 .../cassandra/db/filter/ClusteringIndexFilter.java |  4 +++-
 .../db/filter/ClusteringIndexSliceFilter.java      |  7 ++++++
 .../cassandra/distributed/test/PagingTest.java     | 26 ++++++++++++++++++++++
 .../test/ReadRepairEmptyRangeTombstonesTest.java   | 20 ++++++++---------
 .../cql3/validation/operations/SelectTest.java     |  2 ++
 9 files changed, 74 insertions(+), 18 deletions(-)

diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 6e2baf6..8e0df45 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -512,7 +512,7 @@ public class SelectStatement implements CQLStatement
             return ReadQuery.empty(table);
 
         ClusteringIndexFilter filter = makeClusteringIndexFilter(options, 
columnFilter);
-        if (filter == null)
+        if (filter == null || filter.isEmpty(table.comparator))
             return ReadQuery.empty(table);
 
         RowFilter rowFilter = getRowFilter(options);
diff --git a/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java 
b/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java
index 217f4f1..2b5b20b 100644
--- a/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java
+++ b/src/java/org/apache/cassandra/db/SinglePartitionReadCommand.java
@@ -382,6 +382,9 @@ public class SinglePartitionReadCommand extends ReadCommand 
implements SinglePar
 
     public PartitionIterator execute(ConsistencyLevel consistency, ClientState 
clientState, long queryStartNanoTime) throws RequestExecutionException
     {
+        if (clusteringIndexFilter.isEmpty(metadata().comparator))
+            return EmptyIterators.partition();
+
         return StorageProxy.read(Group.one(this), consistency, clientState, 
queryStartNanoTime);
     }
 
diff --git a/src/java/org/apache/cassandra/db/Slice.java 
b/src/java/org/apache/cassandra/db/Slice.java
index 1aaf430..8956bd1 100644
--- a/src/java/org/apache/cassandra/db/Slice.java
+++ b/src/java/org/apache/cassandra/db/Slice.java
@@ -195,26 +195,37 @@ public class Slice
         if (reversed)
         {
             int cmp = comparator.compare(lastReturned, start);
-            if (cmp < 0 || (!inclusive && cmp == 0))
+            assert cmp != 0;
+            // start is > than lastReturned; got nothing to return no more
+            if (cmp < 0)
                 return null;
 
             cmp = comparator.compare(end, lastReturned);
-            if (cmp < 0 || (inclusive && cmp == 0))
+            assert cmp != 0;
+            if (cmp < 0)
                 return this;
 
-            return new Slice(start, inclusive ? 
ClusteringBound.inclusiveEndOf(lastReturned) : 
ClusteringBound.exclusiveEndOf(lastReturned));
+            Slice slice = new Slice(start, inclusive ? 
ClusteringBound.inclusiveEndOf(lastReturned) : 
ClusteringBound.exclusiveEndOf(lastReturned));
+            if (slice.isEmpty(comparator))
+                return null;
+            return slice;
         }
         else
         {
             int cmp = comparator.compare(end, lastReturned);
-            if (cmp < 0 || (!inclusive && cmp == 0))
+            assert cmp != 0;
+            if (cmp < 0)
                 return null;
 
             cmp = comparator.compare(lastReturned, start);
-            if (cmp < 0 || (inclusive && cmp == 0))
+            assert cmp != 0;
+            if (cmp < 0)
                 return this;
 
-            return new Slice(inclusive ? 
ClusteringBound.inclusiveStartOf(lastReturned) : 
ClusteringBound.exclusiveStartOf(lastReturned), end);
+            Slice slice = new Slice(inclusive ? 
ClusteringBound.inclusiveStartOf(lastReturned) : 
ClusteringBound.exclusiveStartOf(lastReturned), end);
+            if (slice.isEmpty(comparator))
+                return null;
+            return slice;
         }
     }
 
diff --git 
a/src/java/org/apache/cassandra/db/filter/AbstractClusteringIndexFilter.java 
b/src/java/org/apache/cassandra/db/filter/AbstractClusteringIndexFilter.java
index c28117c..63c2783 100644
--- a/src/java/org/apache/cassandra/db/filter/AbstractClusteringIndexFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/AbstractClusteringIndexFilter.java
@@ -42,6 +42,11 @@ public abstract class AbstractClusteringIndexFilter 
implements ClusteringIndexFi
         return reversed;
     }
 
+    public boolean isEmpty(ClusteringComparator comparator)
+    {
+        return false;
+    }
+
     protected abstract void serializeInternal(DataOutputPlus out, int version) 
throws IOException;
     protected abstract long serializedSizeInternal(int version);
 
diff --git a/src/java/org/apache/cassandra/db/filter/ClusteringIndexFilter.java 
b/src/java/org/apache/cassandra/db/filter/ClusteringIndexFilter.java
index d3c6826..6ea0435 100644
--- a/src/java/org/apache/cassandra/db/filter/ClusteringIndexFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/ClusteringIndexFilter.java
@@ -37,7 +37,7 @@ import org.apache.cassandra.schema.TableMetadata;
  */
 public interface ClusteringIndexFilter
 {
-    public static Serializer serializer = 
AbstractClusteringIndexFilter.serializer;
+    public static final Serializer serializer = 
AbstractClusteringIndexFilter.serializer;
 
     public enum Kind
     {
@@ -64,6 +64,8 @@ public interface ClusteringIndexFilter
      */
     public boolean isReversed();
 
+    public boolean isEmpty(ClusteringComparator comparator);
+
     /**
      * Returns a filter for continuing the paging of this filter given the 
last returned clustering prefix.
      *
diff --git 
a/src/java/org/apache/cassandra/db/filter/ClusteringIndexSliceFilter.java 
b/src/java/org/apache/cassandra/db/filter/ClusteringIndexSliceFilter.java
index 6711325..5df98c3 100644
--- a/src/java/org/apache/cassandra/db/filter/ClusteringIndexSliceFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/ClusteringIndexSliceFilter.java
@@ -56,6 +56,13 @@ public class ClusteringIndexSliceFilter extends 
AbstractClusteringIndexFilter
         return slices.size() == 1 && !slices.hasLowerBound() && 
!slices.hasUpperBound();
     }
 
+    // Whether or not it is guaranteed that slices are empty. Since we'd like 
to avoid iteration in general case,
+    // we rely on Slices#forPaging and SelectStatement#makeSlices to skip 
empty bounds.
+    public boolean isEmpty(ClusteringComparator comparator)
+    {
+        return slices.isEmpty();
+    }
+
     public boolean selects(Clustering<?> clustering)
     {
         return slices.selects(clustering);
diff --git 
a/test/distributed/org/apache/cassandra/distributed/test/PagingTest.java 
b/test/distributed/org/apache/cassandra/distributed/test/PagingTest.java
index c0d8fe3..3dcb490 100644
--- a/test/distributed/org/apache/cassandra/distributed/test/PagingTest.java
+++ b/test/distributed/org/apache/cassandra/distributed/test/PagingTest.java
@@ -18,15 +18,18 @@
 
 package org.apache.cassandra.distributed.test;
 
+import java.io.IOException;
 import java.util.Iterator;
 
 import com.google.common.collect.Iterators;
 import org.junit.Test;
 
 import org.apache.cassandra.distributed.Cluster;
+import org.apache.cassandra.distributed.api.ConsistencyLevel;
 
 import static org.apache.cassandra.distributed.api.ConsistencyLevel.QUORUM;
 import static org.apache.cassandra.distributed.shared.AssertUtils.assertRows;
+import static org.apache.cassandra.distributed.shared.AssertUtils.row;
 
 public class PagingTest extends TestBaseImpl
 {
@@ -78,4 +81,27 @@ public class PagingTest extends TestBaseImpl
             }
         }
     }
+
+    @Test
+    public void testPagingWithRangeTombstones() throws IOException
+    {
+        try (Cluster cluster = init(Cluster.build(2).start()))
+        {
+            cluster.schemaChange("CREATE TABLE " + KEYSPACE + ".tbl (pk int, 
ck int, regular int, PRIMARY KEY (pk, ck))");
+            cluster.coordinator(1).execute("DELETE FROM " + KEYSPACE + ".tbl 
WHERE pk = 1 AND ck > 1 AND ck < 10", ConsistencyLevel.ALL);
+            cluster.coordinator(1).execute("insert into " + KEYSPACE + ".tbl 
(pk, ck, regular) values (1,1,1)", ConsistencyLevel.ALL);
+            cluster.coordinator(1).execute("insert into " + KEYSPACE + ".tbl 
(pk, ck, regular) values (1,2,2)", ConsistencyLevel.ALL);
+            cluster.coordinator(1).execute("insert into " + KEYSPACE + ".tbl 
(pk, ck, regular) values (1,3,3)", ConsistencyLevel.ALL);
+            cluster.forEach((node) -> node.flush(KEYSPACE));
+            Iterator<Object[]> iter = 
cluster.coordinator(1).executeWithPaging("SELECT pk,ck,regular FROM " + 
KEYSPACE + ".tbl " +
+                                                                               
"WHERE pk=? AND ck>=? ORDER BY ck DESC;",
+                                                                               
ConsistencyLevel.QUORUM, 1,
+                                                                               
1, 1);
+
+            assertRows(iter,
+                       row(1, 3, 3),
+                       row(1, 2, 2),
+                       row(1, 1, 1));
+        }
+    }
 }
diff --git 
a/test/distributed/org/apache/cassandra/distributed/test/ReadRepairEmptyRangeTombstonesTest.java
 
b/test/distributed/org/apache/cassandra/distributed/test/ReadRepairEmptyRangeTombstonesTest.java
index bcc77e0..c07b128 100644
--- 
a/test/distributed/org/apache/cassandra/distributed/test/ReadRepairEmptyRangeTombstonesTest.java
+++ 
b/test/distributed/org/apache/cassandra/distributed/test/ReadRepairEmptyRangeTombstonesTest.java
@@ -135,11 +135,11 @@ public class ReadRepairEmptyRangeTombstonesTest extends 
TestBaseImpl
                              "WITH CLUSTERING ORDER BY (c %s) AND 
read_repair='%s'")
                 .mutate(1, "DELETE FROM %s USING TIMESTAMP 1 WHERE k=0 AND c>0 
AND c<3")
                 .mutate(1, "INSERT INTO %s (k, s) VALUES (0, 0)")
-                .assertRowsDistributed("SELECT * FROM %s WHERE k=0 AND c>1 and 
c<=1", 1)
+                .assertRowsDistributed("SELECT * FROM %s WHERE k=0 AND c>1 and 
c<=1", 0)
                 .assertRowsDistributed("SELECT * FROM %s WHERE k=0 AND c>2 and 
c<=2", 0)
-                .assertRowsInternal("SELECT * FROM %s", row(0, null, 0))
+                .assertRowsInternal("SELECT * FROM %s")
                 .mutate(2, "DELETE FROM %s WHERE k=0 AND c>0 AND c<3")
-                .assertRowsInternal("SELECT * FROM %s", row(0, null, 0));
+                .assertRowsInternal("SELECT * FROM %s");
     }
 
     /**
@@ -155,10 +155,10 @@ public class ReadRepairEmptyRangeTombstonesTest extends 
TestBaseImpl
                 .mutate(1, "INSERT INTO %s (k, c) VALUES (0, 3)")
                 .mutate(1, "INSERT INTO %s (k, c) VALUES (0, 4)")
                 .assertRowsDistributed("SELECT * FROM %s WHERE k=0 AND c>=2 
AND c<=3",
-                                       paging ? (flush ? 3 : 2) : 1,
+                                       paging ? 2 : 1,
                                        row(0, 2), row(0, 3))
                 .assertRowsDistributed("SELECT * FROM %s WHERE k=0 AND c>=3 
AND c<=4",
-                                       paging && flush ? 2 : 1,
+                                       1,
                                        row(0, 3), row(0, 4))
                 .assertRowsInternal("SELECT * FROM %s", row(0, 2), row(0, 3), 
row(0, 4))
                 .mutate(2, "DELETE FROM %s WHERE k=0 AND c>=1 AND c<=5")
@@ -169,7 +169,7 @@ public class ReadRepairEmptyRangeTombstonesTest extends 
TestBaseImpl
      * Test range queries asking for a not-empty range targeting rows 
overlapping with a tombstone range in one replica.
      */
     @Test
-    public void testRangeQueriesWithRowsOverlappingWithTombstoneRangeStart()
+    public void testRangeQueriesWithRowsOvetrlappingWithTombstoneRangeStart()
     {
         tester().createTable("CREATE TABLE %s(k int, c int, PRIMARY KEY (k, 
c)) " +
                              "WITH CLUSTERING ORDER BY (c %s) AND 
read_repair='%s'")
@@ -181,10 +181,10 @@ public class ReadRepairEmptyRangeTombstonesTest extends 
TestBaseImpl
                 .mutate(1, "INSERT INTO %s (k, c) VALUES (0, 5)")
                 .mutate(1, "INSERT INTO %s (k, c) VALUES (0, 6)")
                 .assertRowsDistributed("SELECT c FROM %s WHERE k=0 AND c>=1 
AND c<=4",
-                                       paging ? (flush && !reverse ? 5 : 4) : 
1,
+                                       paging ? 4 : 1,
                                        row(1), row(2), row(3), row(4))
                 .assertRowsDistributed("SELECT c FROM %s WHERE k=0 AND c>=2 
AND c<=5",
-                                       paging && flush && !reverse ? 2 : 1,
+                                       1,
                                        row(2), row(3), row(4), row(5))
                 .assertRowsInternal("SELECT c FROM %s", row(1), row(2), 
row(3), row(4), row(5))
                 .mutate(2, "DELETE FROM %s WHERE k=0 AND c>=1 AND c<=6")
@@ -207,10 +207,10 @@ public class ReadRepairEmptyRangeTombstonesTest extends 
TestBaseImpl
                 .mutate(1, "INSERT INTO %s (k, c) VALUES (0, 5)")
                 .mutate(1, "INSERT INTO %s (k, c) VALUES (0, 6)")
                 .assertRowsDistributed("SELECT c FROM %s WHERE k=0 AND c>=2 
AND c<=5",
-                                       paging ? (flush && reverse ? 5 : 4) : 1,
+                                       paging ? 4 : 1,
                                        row(2), row(3), row(4), row(5))
                 .assertRowsDistributed("SELECT c FROM %s WHERE k=0 AND c>=3 
AND c<=6",
-                                       paging && flush && reverse ? 2 : 1,
+                                       1,
                                        row(3), row(4), row(5), row(6))
                 .assertRowsInternal("SELECT c FROM %s", row(2), row(3), 
row(4), row(5), row(6))
                 .mutate(2, "DELETE FROM %s WHERE k=0 AND c>=1 AND c<=6")
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 d7c1e25..d0493cf 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@ -2472,6 +2472,8 @@ public class SelectTest extends CQLTester
             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 c2 < 1 AND v = 3 ALLOW FILTERING;"));
+
             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));
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to