diff --git a/CHANGES.txt b/CHANGES.txt index 4fa6658839..2572c1f313 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -182,6 +182,7 @@ * Add the ability to disable bulk loading of SSTables (CASSANDRA-18781) * Clean up obsolete functions and simplify cql_version handling in cqlsh (CASSANDRA-18787) Merged from 5.0: + * Ensure RowFilter#isMutableIntersection() properly evaluates numeric ranges on a single column (CASSANDRA-20566) * SAI marks an index as non-empty when a partial partition/row modifications is flushed due to repair (CASSANDRA-20567) * SAI fails queries when multiple columns exist and a non-indexed column is a composite with a map (CASSANDRA-19891) * Avoid purging deletions in RowFilter when reconciliation is required (CASSANDRA-20541) diff --git a/src/java/org/apache/cassandra/db/filter/RowFilter.java b/src/java/org/apache/cassandra/db/filter/RowFilter.java index 7dc1c0c07d..d60dd43b99 100644 --- a/src/java/org/apache/cassandra/db/filter/RowFilter.java +++ b/src/java/org/apache/cassandra/db/filter/RowFilter.java @@ -21,8 +21,10 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; @@ -172,15 +174,21 @@ public class RowFilter implements Iterable */ public boolean isMutableIntersection() { - int count = 0; + Set columns = null; for (Expression e : expressions) { if (e.column.isStatic() && expressions.size() > 1) return true; if (!e.column.isPrimaryKeyColumn()) - if (++count > 1) + { + if (columns == null) + columns = new HashSet<>(expressions.size()); + + columns.add(e.column); + if (columns.size() > 1) return true; + } } return false; } diff --git a/test/unit/org/apache/cassandra/db/filter/RowFilterTest.java b/test/unit/org/apache/cassandra/db/filter/RowFilterTest.java index 6cd08d0001..d73f796c90 100644 --- a/test/unit/org/apache/cassandra/db/filter/RowFilterTest.java +++ b/test/unit/org/apache/cassandra/db/filter/RowFilterTest.java @@ -20,9 +20,10 @@ package org.apache.cassandra.db.filter; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; -import org.junit.Assert; +import com.google.common.collect.ImmutableList; import org.junit.Test; import org.apache.cassandra.cql3.ColumnIdentifier; @@ -33,6 +34,7 @@ import org.apache.cassandra.db.DeletionTime; import org.apache.cassandra.db.LivenessInfo; import org.apache.cassandra.db.RegularAndStaticColumns; import org.apache.cassandra.db.marshal.Int32Type; +import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.db.partitions.SingletonUnfilteredPartitionIterator; import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator; import org.apache.cassandra.db.rows.BTreeRow; @@ -45,8 +47,12 @@ import org.apache.cassandra.db.rows.Unfiltered; import org.apache.cassandra.db.rows.UnfilteredRowIterator; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.TableMetadata; +import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.btree.BTree; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + public class RowFilterTest { @@ -89,8 +95,8 @@ public class RowFilterTest closed.set(true); } }), 1); - Assert.assertFalse(iter.hasNext()); - Assert.assertTrue(closed.get()); + assertFalse(iter.hasNext()); + assertTrue(closed.get()); filter = RowFilter.none().withNewExpressions(new ArrayList<>()); filter.add(r, Operator.NEQ, one); @@ -123,9 +129,35 @@ public class RowFilterTest closed.set(true); } }), 1); - Assert.assertFalse(iter.hasNext()); - Assert.assertTrue(closed.get()); + assertFalse(iter.hasNext()); + assertTrue(closed.get()); } + @Test + public void testMutableIntersections() + { + TableMetadata metadata = TableMetadata.builder("testks", "testcf") + .addPartitionKeyColumn("pk", Int32Type.instance) + .addRegularColumn("r", Int32Type.instance) + .addRegularColumn("t", UTF8Type.instance) + .offline() + .build(); + RowFilter filter = RowFilter.none().withNewExpressions(new ArrayList<>()); + assertFalse(filter.isMutableIntersection()); + + ColumnMetadata r = metadata.getColumn(new ColumnIdentifier("r", true)); + RowFilter.Expression gt = new RowFilter.SimpleExpression(r, Operator.GT, ByteBufferUtil.EMPTY_BYTE_BUFFER); + filter = filter.withNewExpressions(Collections.singletonList(gt)); + assertFalse(filter.isMutableIntersection()); + + RowFilter.Expression lt = new RowFilter.SimpleExpression(r, Operator.LT, ByteBufferUtil.EMPTY_BYTE_BUFFER); + filter = filter.withNewExpressions(ImmutableList.of(gt, lt)); + assertFalse(filter.isMutableIntersection()); + + ColumnMetadata t = metadata.getColumn(new ColumnIdentifier("t", true)); + RowFilter.Expression eq = new RowFilter.SimpleExpression(t, Operator.EQ, ByteBufferUtil.EMPTY_BYTE_BUFFER); + filter = filter.withNewExpressions(ImmutableList.of(gt, lt, eq)); + assertTrue(filter.isMutableIntersection()); + } }