From 3f49c328f202e68b67a9caaa63522e333ea5006f Mon Sep 17 00:00:00 2001 From: ZhaoYang Date: Mon, 12 Sep 2016 11:22:25 +0200 Subject: [PATCH] Allow filtering on partition key columns for queries without secondary indexes patch by ZhaoYang and Alex Petrov; reviewed by Benjamin Lerer for CASSANDRA-11031 --- CHANGES.txt | 1 + NEWS.txt | 2 + .../cassandra/cql3/SingleColumnRelation.java | 11 - .../PartitionKeyRestrictions.java | 17 + .../PartitionKeySingleRestrictionSet.java | 26 + .../cql3/restrictions/RestrictionSet.java | 10 + .../restrictions/RestrictionSetWrapper.java | 5 + .../cql3/restrictions/Restrictions.java | 6 + .../restrictions/StatementRestrictions.java | 65 +- .../cql3/restrictions/TokenFilter.java | 23 +- .../cql3/restrictions/TokenRestriction.java | 25 +- .../apache/cassandra/db/filter/RowFilter.java | 30 +- .../cassandra/cql3/ViewFilteringTest.java | 211 +++- .../org/apache/cassandra/cql3/ViewTest.java | 13 + .../entities/SecondaryIndexTest.java | 95 ++ .../SelectMultiColumnRelationTest.java | 11 +- .../SelectOrderedPartitionerTest.java | 34 +- .../SelectSingleColumnRelationTest.java | 28 +- .../validation/operations/SelectTest.java | 1109 ++++++++++++++++- .../index/internal/CassandraIndexTest.java | 4 +- 20 files changed, 1638 insertions(+), 88 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3ab144e5d1..312713f60a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.10 + * Allow filtering on partition key columns for queries without secondary indexes (CASSANDRA-11031) * Fix Cassandra Stress reporting thread model and precision (CASSANDRA-12585) * Add JMH benchmarks.jar (CASSANDRA-12586) * Add row offset support to SASI (CASSANDRA-11990) diff --git a/NEWS.txt b/NEWS.txt index ddb1263312..1b15f7d74b 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -18,6 +18,8 @@ using the provided 'sstableupgrade' tool. New features ------------ + - Filtering on partition key columns is now also supported for queries without + secondary indexes. - A slow query log has been added: slow queries will be logged at DEBUG level. For more details refer to CASSANDRA-12403 and slow_query_log_timeout_in_ms in cassandra.yaml. diff --git a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java index 22df6bd0b3..4dbb7daf7a 100644 --- a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java +++ b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java @@ -252,17 +252,6 @@ public final class SingleColumnRelation extends Relation checkFalse(!columnDef.isPrimaryKeyColumn() && !canHaveOnlyOneValue(), "IN predicates on non-primary-key columns (%s) is not yet supported", columnDef.name); } - else if (isSlice()) - { - // Non EQ relation is not supported without token(), even if we have a 2ndary index (since even those - // are ordered by partitioner). - // Note: In theory we could allow it for 2ndary index queries with ALLOW FILTERING, but that would - // probably require some special casing - // Note bis: This is also why we don't bother handling the 'tuple' notation of #4851 for keys. If we - // lift the limitation for 2ndary - // index with filtering, we'll need to handle it though. - checkFalse(columnDef.isPartitionKey(), "Only EQ and IN relation are supported on the partition key (unless you use the token() function)"); - } checkFalse(isContainsKey() && !(receiver.type instanceof MapType), "Cannot use CONTAINS KEY on non-map column %s", receiver.name); checkFalse(isContains() && !(receiver.type.isCollection()), "Cannot use CONTAINS on non-collection column %s", receiver.name); diff --git a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java index 10efa9f414..1ff45d05bf 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.restrictions; import java.nio.ByteBuffer; import java.util.List; +import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.statements.Bound; @@ -48,4 +49,20 @@ interface PartitionKeyRestrictions extends Restrictions * @return true if the specified bound is inclusive, false otherwise */ public boolean isInclusive(Bound b); + + /** + * checks if specified restrictions require filtering + * + * @param cfm column family metadata + * @return true if filtering is required, false otherwise + */ + public boolean needFiltering(CFMetaData cfm); + + /** + * Checks if the partition key has unrestricted components. + * + * @param cfm column family metadata + * @return true if the partition key has unrestricted components, false otherwise. + */ + public boolean hasUnrestrictedPartitionKeyComponents(CFMetaData cfm); } diff --git a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java index b96f6dab46..b34ff54588 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java @@ -129,4 +129,30 @@ final class PartitionKeySingleRestrictionSet extends RestrictionSetWrapper imple restriction.addRowFilterTo(filter, indexManager, options); } } + + @Override + public boolean needFiltering(CFMetaData cfm) + { + if (isEmpty()) + return false; + // slice or has unrestricted key component + return hasUnrestrictedPartitionKeyComponents(cfm) || hasSlice(); + } + + @Override + public boolean hasUnrestrictedPartitionKeyComponents(CFMetaData cfm) + { + return size() < cfm.partitionKeyColumns().size(); + } + + @Override + public boolean hasSlice() + { + for (SingleRestriction restriction : restrictions) + { + if (restriction.isSlice()) + return true; + } + return false; + } } diff --git a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java index 0e6c2f73f2..0876f3ec44 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java @@ -266,6 +266,16 @@ final class RestrictionSet implements Restrictions, Iterable return false; } + public final boolean hasSlice() + { + for (SingleRestriction restriction : this) + { + if (restriction.isSlice()) + return true; + } + return false; + } + /** * Checks if all of the underlying restrictions are EQ or IN restrictions. * diff --git a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java index 6b110da6b2..996a1c45c7 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java @@ -89,6 +89,11 @@ class RestrictionSetWrapper implements Restrictions return restrictions.hasIN(); } + public boolean hasSlice() + { + return restrictions.hasSlice(); + } + public boolean hasOnlyEqualityRestrictions() { return restrictions.hasOnlyEqualityRestrictions(); diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java index 4cf165fdc1..8a5140a3ea 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java @@ -54,6 +54,12 @@ public interface Restrictions extends Restriction */ public boolean hasIN(); + /** + * Checks if any of the underlying restrictions is a slice. + * @return true if any of the underlying restrictions is a slice, false otherwise + */ + public boolean hasSlice(); + /** * Checks if all of the underlying restrictions are EQ or IN restrictions. * diff --git a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java index 0b3b37d802..53ac68c077 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java @@ -190,11 +190,11 @@ public final class StatementRestrictions } // At this point, the select statement if fully constructed, but we still have a few things to validate - processPartitionKeyRestrictions(hasQueriableIndex); + processPartitionKeyRestrictions(hasQueriableIndex, allowFiltering, forView); // Some but not all of the partition key columns have been specified; // hence we need turn these restrictions into a row filter. - if (usesSecondaryIndexing) + if (usesSecondaryIndexing || partitionKeyRestrictions.needFiltering(cfm)) filterRestrictions.add(partitionKeyRestrictions); if (selectsOnlyStaticColumns && hasClusteringColumnsRestriction()) @@ -385,36 +385,48 @@ public final class StatementRestrictions return this.usesSecondaryIndexing; } - private void processPartitionKeyRestrictions(boolean hasQueriableIndex) + private void processPartitionKeyRestrictions(boolean hasQueriableIndex, boolean allowFiltering, boolean forView) { if (!type.allowPartitionKeyRanges()) { checkFalse(partitionKeyRestrictions.isOnToken(), "The token function cannot be used in WHERE clauses for %s statements", type); - if (hasUnrestrictedPartitionKeyComponents()) + if (partitionKeyRestrictions.hasUnrestrictedPartitionKeyComponents(cfm)) throw invalidRequest("Some partition key parts are missing: %s", Joiner.on(", ").join(getPartitionKeyUnrestrictedComponents())); + + // slice query + checkFalse(partitionKeyRestrictions.hasSlice(), + "Only EQ and IN relation are supported on the partition key (unless you use the token() function)" + + " for %s statements", type); } else { - // If there is a queriable index, no special condition are required on the other restrictions. - // But we still need to know 2 things: - // - If we don't have a queriable index, is the query ok - // - Is it queriable without 2ndary index, which is always more efficient - // If a component of the partition key is restricted by a relation, all preceding - // components must have a EQ. Only the last partition key component can be in IN relation. - if (partitionKeyRestrictions.isOnToken()) - isKeyRange = true; + // If there are no partition restrictions or there's only token restriction, we have to set a key range + if (partitionKeyRestrictions.isOnToken()) + isKeyRange = true; - if (hasUnrestrictedPartitionKeyComponents()) + if (partitionKeyRestrictions.isEmpty() && partitionKeyRestrictions.hasUnrestrictedPartitionKeyComponents(cfm)) { - if (!partitionKeyRestrictions.isEmpty()) - { - if (!hasQueriableIndex) - throw invalidRequest("Partition key parts: %s must be restricted as other parts are", - Joiner.on(", ").join(getPartitionKeyUnrestrictedComponents())); - } + isKeyRange = true; + usesSecondaryIndexing = hasQueriableIndex; + } + + // If there is a queriable index, no special condition is required on the other restrictions. + // But we still need to know 2 things: + // - If we don't have a queriable index, is the query ok + // - Is it queriable without 2ndary index, which is always more efficient + // If a component of the partition key is restricted by a relation, all preceding + // components must have a EQ. Only the last partition key component can be in IN relation. + if (partitionKeyRestrictions.needFiltering(cfm)) + { + if (!allowFiltering && !forView && !hasQueriableIndex + && (partitionKeyRestrictions.hasUnrestrictedPartitionKeyComponents(cfm) || partitionKeyRestrictions.hasSlice())) + throw new InvalidRequestException(REQUIRES_ALLOW_FILTERING_MESSAGE); + + if (partitionKeyRestrictions.hasIN()) + throw new InvalidRequestException("IN restrictions are not supported when the query involves filtering"); isKeyRange = true; usesSecondaryIndexing = hasQueriableIndex; @@ -422,15 +434,6 @@ public final class StatementRestrictions } } - /** - * Checks if the partition key has some unrestricted components. - * @return true if the partition key has some unrestricted components, false otherwise. - */ - private boolean hasUnrestrictedPartitionKeyComponents() - { - return partitionKeyRestrictions.size() < cfm.partitionKeyColumns().size(); - } - public boolean hasPartitionKeyRestrictions() { return !partitionKeyRestrictions.isEmpty(); @@ -622,8 +625,8 @@ public final class StatementRestrictions private ByteBuffer getPartitionKeyBound(Bound b, QueryOptions options) { // Deal with unrestricted partition key components (special-casing is required to deal with 2i queries on the - // first component of a composite partition key). - if (hasUnrestrictedPartitionKeyComponents()) + // first component of a composite partition key) queries that filter on the partition key. + if (partitionKeyRestrictions.needFiltering(cfm)) return ByteBufferUtil.EMPTY_BYTE_BUFFER; // We deal with IN queries for keys in other places, so we know buildBound will return only one result @@ -804,7 +807,7 @@ public final class StatementRestrictions public boolean hasAllPKColumnsRestrictedByEqualities() { return !isPartitionKeyRestrictionsOnToken() - && !hasUnrestrictedPartitionKeyComponents() + && !partitionKeyRestrictions.hasUnrestrictedPartitionKeyComponents(cfm) && (partitionKeyRestrictions.hasOnlyEqualityRestrictions()) && !hasUnrestrictedClusteringColumns() && (clusteringColumnsRestrictions.hasOnlyEqualityRestrictions()); diff --git a/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java b/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java index c27d7426a4..2611d198b0 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenFilter.java @@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableRangeSet; import com.google.common.collect.Range; import com.google.common.collect.RangeSet; +import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.functions.Function; @@ -135,8 +136,8 @@ final class TokenFilter implements PartitionKeyRestrictions */ private List filter(List values, QueryOptions options) throws InvalidRequestException { - RangeSet rangeSet = tokenRestriction.isSlice() ? toRangeSet(tokenRestriction, options) - : toRangeSet(tokenRestriction.values(options)); + RangeSet rangeSet = tokenRestriction.hasSlice() ? toRangeSet(tokenRestriction, options) + : toRangeSet(tokenRestriction.values(options)); return filterWithRangeSet(rangeSet, values); } @@ -285,4 +286,22 @@ final class TokenFilter implements PartitionKeyRestrictions { return restrictions.size(); } + + @Override + public boolean needFiltering(CFMetaData cfm) + { + return restrictions.needFiltering(cfm); + } + + @Override + public boolean hasUnrestrictedPartitionKeyComponents(CFMetaData cfm) + { + return restrictions.hasUnrestrictedPartitionKeyComponents(cfm); + } + + @Override + public boolean hasSlice() + { + return restrictions.hasSlice(); + } } diff --git a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java index dd31730f65..e90319d667 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java @@ -57,11 +57,6 @@ public abstract class TokenRestriction implements PartitionKeyRestrictions this.metadata = metadata; } - public boolean isSlice() - { - return false; - } - public boolean hasIN() { return false; @@ -84,6 +79,24 @@ public abstract class TokenRestriction implements PartitionKeyRestrictions return true; } + @Override + public boolean needFiltering(CFMetaData cfm) + { + return false; + } + + @Override + public boolean hasSlice() + { + return false; + } + + @Override + public boolean hasUnrestrictedPartitionKeyComponents(CFMetaData cfm) + { + return false; + } + @Override public List getColumnDefs() { @@ -225,7 +238,7 @@ public abstract class TokenRestriction implements PartitionKeyRestrictions } @Override - public boolean isSlice() + public boolean hasSlice() { return true; } diff --git a/src/java/org/apache/cassandra/db/filter/RowFilter.java b/src/java/org/apache/cassandra/db/filter/RowFilter.java index 6626275642..4c0608f11c 100644 --- a/src/java/org/apache/cassandra/db/filter/RowFilter.java +++ b/src/java/org/apache/cassandra/db/filter/RowFilter.java @@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.base.Objects; +import com.google.common.collect.Iterables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -274,9 +275,19 @@ public abstract class RowFilter implements Iterable return iter; final CFMetaData metadata = iter.metadata(); - long numberOfStaticColumnExpressions = expressions.stream().filter(e -> e.column.isStatic()).count(); - final boolean filterStaticColumns = numberOfStaticColumnExpressions != 0; - final boolean filterNonStaticColumns = (expressions.size() - numberOfStaticColumnExpressions) > 0; + + List partitionLevelExpressions = new ArrayList<>(); + List rowLevelExpressions = new ArrayList<>(); + for (Expression e: expressions) + { + if (e.column.isStatic() || e.column.isPartitionKey()) + partitionLevelExpressions.add(e); + else + rowLevelExpressions.add(e); + } + + long numberOfRegularColumnExpressions = rowLevelExpressions.size(); + final boolean filterNonStaticColumns = numberOfRegularColumnExpressions > 0; class IsSatisfiedFilter extends Transformation { @@ -285,9 +296,10 @@ public abstract class RowFilter implements Iterable { pk = partition.partitionKey(); - // The filter might be on static columns, so need to check static row first. - if (filterStaticColumns && applyToRow(partition.staticRow()) == null) - return null; + // Short-circuit all partitions that won't match based on static and partition keys + for (Expression e : partitionLevelExpressions) + if (!e.isSatisfiedBy(metadata, partition.partitionKey(), partition.staticRow())) + return null; UnfilteredRowIterator iterator = Transformation.apply(partition, this); return (filterNonStaticColumns && !iterator.hasNext()) ? null : iterator; @@ -299,9 +311,10 @@ public abstract class RowFilter implements Iterable if (purged == null) return null; - for (Expression e : expressions) + for (Expression e : rowLevelExpressions) if (!e.isSatisfiedBy(metadata, pk, purged)) return null; + return row; } } @@ -669,9 +682,6 @@ public abstract class RowFilter implements Iterable // TODO: we should try to merge both code someday. assert value != null; - if (row.isStatic() != column.isStatic()) - return true; - switch (operator) { case EQ: diff --git a/test/unit/org/apache/cassandra/cql3/ViewFilteringTest.java b/test/unit/org/apache/cassandra/cql3/ViewFilteringTest.java index 12cb6734f0..0cecfa2cc4 100644 --- a/test/unit/org/apache/cassandra/cql3/ViewFilteringTest.java +++ b/test/unit/org/apache/cassandra/cql3/ViewFilteringTest.java @@ -148,13 +148,6 @@ public class ViewFilteringTest extends CQLTester throw new RuntimeException("MV alter failed: " + goodStatements.get(i), e); } } - - try - { - createView("mv_foo", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE a = 1 AND b IS NOT NULL AND c IS NOT NULL AND d is NOT NULL PRIMARY KEY ((a, b), c, d)"); - Assert.fail("Partial partition key restriction should not be allowed"); - } - catch (InvalidQueryException exc) {} } @Test @@ -265,6 +258,210 @@ public class ViewFilteringTest extends CQLTester ); } + @Test + public void testPartitionKeyFilteringUnrestrictedPart() throws Throwable + { + List mvPrimaryKeys = Arrays.asList("((a, b), c)", "((b, a), c)", "(a, b, c)", "(c, b, a)", "((c, a), b)"); + for (int i = 0; i < mvPrimaryKeys.size(); i++) + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY ((a, b), c))"); + + execute("USE " + keyspace()); + executeNet(protocolVersion, "USE " + keyspace()); + + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 0, 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 0, 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, 0, 0, 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, 0, 1, 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, 1, 0, 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, 1, 1, 0); + + logger.info("Testing MV primary key: {}", mvPrimaryKeys.get(i)); + + // only accept rows where a = 1 + String viewName= "mv_test" + i; + createView(viewName, "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE a = 1 AND b IS NOT NULL AND c IS NOT NULL PRIMARY KEY " + mvPrimaryKeys.get(i)); + + waitForView(keyspace(), viewName); + + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 0, 0, 0), + row(1, 0, 1, 0), + row(1, 1, 0, 0), + row(1, 1, 1, 0) + ); + + // insert new rows that do not match the filter + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 2, 0, 0, 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 2, 1, 0, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 0, 0, 0), + row(1, 0, 1, 0), + row(1, 1, 0, 0), + row(1, 1, 1, 0) + ); + + // insert new row that does match the filter + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, 1, 2, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 0, 0, 0), + row(1, 0, 1, 0), + row(1, 1, 0, 0), + row(1, 1, 1, 0), + row(1, 1, 2, 0) + ); + + // update rows that don't match the filter + execute("UPDATE %s SET d = ? WHERE a = ? AND b = ? AND c = ?", 1, 0, 0, 0); + execute("UPDATE %s SET d = ? WHERE a = ? AND b = ? AND c = ?", 1, 0, 1, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 0, 0, 0), + row(1, 0, 1, 0), + row(1, 1, 0, 0), + row(1, 1, 1, 0), + row(1, 1, 2, 0) + ); + + // update a row that does match the filter + execute("UPDATE %s SET d = ? WHERE a = ? AND b = ? AND c = ?", 1, 1, 1, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 0, 0, 0), + row(1, 0, 1, 0), + row(1, 1, 0, 1), + row(1, 1, 1, 0), + row(1, 1, 2, 0) + ); + + // delete rows that don't match the filter + execute("DELETE FROM %s WHERE a = ? AND b = ? AND c = ?", 0, 0, 0); + execute("DELETE FROM %s WHERE a = ? AND b = ? AND c = ?", 0, 1, 0); + execute("DELETE FROM %s WHERE a = ? AND b = ?", 0, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 0, 0, 0), + row(1, 0, 1, 0), + row(1, 1, 0, 1), + row(1, 1, 1, 0), + row(1, 1, 2, 0) + ); + + // delete a row that does match the filter + execute("DELETE FROM %s WHERE a = ? AND b = ? AND c = ?", 1, 1, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 0, 0, 0), + row(1, 0, 1, 0), + row(1, 1, 1, 0), + row(1, 1, 2, 0) + ); + + // delete a partition that matches the filter + execute("DELETE FROM %s WHERE a = ? AND b = ?", 1, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 1, 1, 0), + row(1, 1, 2, 0)); + execute("DELETE FROM %s WHERE a = ? AND b = ?", 1, 1); + assertEmpty(execute("SELECT * FROM mv_test" + i)); + } + } + + @Test + public void testPartitionKeyFilteringWithSlice() throws Throwable + { + List mvPrimaryKeys = Arrays.asList("((a, b), c)", "((b, a), c)", "(a, b, c)", "(c, b, a)", "((c, a), b)"); + for (int i = 0; i < mvPrimaryKeys.size(); i++) + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY ((a, b), c))"); + + execute("USE " + keyspace()); + executeNet(protocolVersion, "USE " + keyspace()); + + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 1, 1); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 10, 1, 2); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, 0, 2, 1); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, 10, 2, 2); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 2, 1, 3, 1); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 2, 10, 3, 2); + + logger.info("Testing MV primary key: {}", mvPrimaryKeys.get(i)); + + // only accept rows where a = 1 + String viewName= "mv_test" + i; + createView(viewName, "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE a > 0 AND b > 5 AND c IS NOT NULL PRIMARY KEY " + mvPrimaryKeys.get(i)); + + waitForView(keyspace(), viewName); + + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 10, 2, 2), + row(2, 10, 3, 2) + ); + + // insert new rows that do not match the filter + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 2, 0, 0, 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 2, 1, 0, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 10, 2, 2), + row(2, 10, 3, 2) + ); + + // insert new row that does match the filter + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 3, 10, 4, 2); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 10, 2, 2), + row(2, 10, 3, 2), + row(3, 10, 4, 2) + ); + + // update rows that don't match the filter + execute("UPDATE %s SET d = ? WHERE a = ? AND b = ? AND c = ?", 1, 0, 0, 0); + execute("UPDATE %s SET d = ? WHERE a = ? AND b = ? AND c = ?", 1, 0, 1, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 10, 2, 2), + row(2, 10, 3, 2), + row(3, 10, 4, 2) + ); + + // update a row that does match the filter + execute("UPDATE %s SET d = ? WHERE a = ? AND b = ? AND c = ?", 100, 3, 10, 4); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 10, 2, 2), + row(2, 10, 3, 2), + row(3, 10, 4, 100) + ); + + // delete rows that don't match the filter + execute("DELETE FROM %s WHERE a = ? AND b = ? AND c = ?", 0, 0, 0); + execute("DELETE FROM %s WHERE a = ? AND b = ? AND c = ?", 0, 1, 0); + execute("DELETE FROM %s WHERE a = ? AND b = ?", 0, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 10, 2, 2), + row(2, 10, 3, 2), + row(3, 10, 4, 100) + ); + + // delete a row that does match the filter + execute("DELETE FROM %s WHERE a = ? AND b = ? AND c = ?", 1, 1, 0); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(1, 10, 2, 2), + row(2, 10, 3, 2), + row(3, 10, 4, 100) + ); + + // delete a partition that matches the filter + execute("DELETE FROM %s WHERE a = ? AND b = ?", 1, 10); + assertRowsIgnoringOrder(execute("SELECT a, b, c, d FROM mv_test" + i), + row(2, 10, 3, 2), + row(3, 10, 4, 100)); + } + } + + + + + private static void waitForView(String keyspace, String view) throws InterruptedException + { + while (!SystemKeyspace.isViewBuilt(keyspace, view)) + Thread.sleep(10); + } + @Test public void testPartitionKeyRestrictions() throws Throwable { diff --git a/test/unit/org/apache/cassandra/cql3/ViewTest.java b/test/unit/org/apache/cassandra/cql3/ViewTest.java index c9ef401e23..09455116da 100644 --- a/test/unit/org/apache/cassandra/cql3/ViewTest.java +++ b/test/unit/org/apache/cassandra/cql3/ViewTest.java @@ -117,6 +117,19 @@ public class ViewTest extends CQLTester Assert.assertEquals(0, execute("select * from view1").size()); } + + @Test + public void createMvWithUnrestrictedPKParts() throws Throwable + { + createTable("CREATE TABLE %s (k1 int, c1 int , val int, PRIMARY KEY (k1, c1))"); + + execute("USE " + keyspace()); + executeNet(protocolVersion, "USE " + keyspace()); + + createView("view1", "CREATE MATERIALIZED VIEW view1 AS SELECT k1 FROM %%s WHERE k1 IS NOT NULL AND c1 IS NOT NULL AND val IS NOT NULL PRIMARY KEY (val, k1, c1)"); + + } + @Test public void testClusteringKeyTombstone() throws Throwable { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java index 2072bf2510..05c11a421a 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java @@ -1177,6 +1177,101 @@ public class SecondaryIndexTest extends CQLTester row(bytes("foo124"), EMPTY_BYTE_BUFFER)); } + @Test + public void testPartitionKeyWithIndex() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, PRIMARY KEY ((a, b)))"); + createIndex("CREATE INDEX ON %s (a);"); + createIndex("CREATE INDEX ON %s (b);"); + + execute("INSERT INTO %s (a, b, c) VALUES (1,2,3)"); + execute("INSERT INTO %s (a, b, c) VALUES (2,3,4)"); + execute("INSERT INTO %s (a, b, c) VALUES (5,6,7)"); + + beforeAndAfterFlush(() -> { + assertRows(execute("SELECT * FROM %s WHERE a = 1"), + row(1, 2, 3)); + assertRows(execute("SELECT * FROM %s WHERE b = 3"), + row(2, 3, 4)); + + }); + } + + @Test + public void testAllowFilteringOnPartitionKeyWithSecondaryIndex() throws Throwable + { + createTable("CREATE TABLE %s (pk1 int, pk2 int, c1 int, c2 int, v int, " + + "PRIMARY KEY ((pk1, pk2), c1, c2))"); + createIndex("CREATE INDEX v_idx_1 ON %s (v);"); + + for (int i = 1; i <= 5; i++) + { + for (int j = 1; j <= 2; j++) + { + execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", j, 1, 1, 1, i); + execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", j, 1, 1, i, i); + execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", j, 1, i, i, i); + execute("INSERT INTO %s (pk1, pk2, c1, c2, v) VALUES (?, ?, ?, ?, ?)", j, i, i, i, i); + } + } + + beforeAndAfterFlush(() -> { + assertEmpty(execute("SELECT * FROM %s WHERE pk1 = 1 AND c1 > 0 AND c1 < 5 AND c2 = 1 AND v = 3 ALLOW FILTERING;")); + + assertRows(execute("SELECT * FROM %s WHERE pk1 = 1 AND c1 > 0 AND c1 < 5 AND c2 = 3 AND v = 3 ALLOW FILTERING;"), + row(1, 3, 3, 3, 3), + row(1, 1, 1, 3, 3), + row(1, 1, 3, 3, 3)); + + assertEmpty(execute("SELECT * FROM %s WHERE pk1 = 1 AND c2 > 1 AND c2 < 5 AND v = 1 ALLOW FILTERING;")); + + assertRows(execute("SELECT * FROM %s WHERE pk1 = 1 AND c1 > 1 AND c2 > 2 AND v = 3 ALLOW FILTERING;"), + row(1, 3, 3, 3, 3), + row(1, 1, 3, 3, 3)); + + assertRows(execute("SELECT * FROM %s WHERE pk1 = 1 AND pk2 > 1 AND c2 > 2 AND v = 3 ALLOW FILTERING;"), + row(1, 3, 3, 3, 3)); + + assertRowsIgnoringOrder(execute("SELECT * FROM %s WHERE pk2 > 1 AND c1 IN(0,1,2) AND v <= 3 ALLOW FILTERING;"), + row(1, 2, 2, 2, 2), + row(2, 2, 2, 2, 2)); + + assertRows(execute("SELECT * FROM %s WHERE pk1 >= 2 AND pk2 <=3 AND c1 IN(0,1,2) AND c2 IN(0,1,2) AND v < 3 ALLOW FILTERING;"), + row(2, 2, 2, 2, 2), + row(2, 1, 1, 2, 2), + row(2, 1, 2, 2, 2)); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE pk1 >= 1 AND pk2 <=3 AND c1 IN(0,1,2) AND c2 IN(0,1,2) AND v = 3"); + }); + } + + @Test + public void testAllowFilteringOnPartitionKeyWithIndexForContains() throws Throwable + { + createTable("CREATE TABLE %s (k1 int, k2 int, v set, PRIMARY KEY ((k1, k2)))"); + createIndex("CREATE INDEX ON %s(k2)"); + + execute("INSERT INTO %s (k1, k2, v) VALUES (?, ?, ?)", 0, 0, set(1, 2, 3)); + execute("INSERT INTO %s (k1, k2, v) VALUES (?, ?, ?)", 0, 1, set(2, 3, 4)); + execute("INSERT INTO %s (k1, k2, v) VALUES (?, ?, ?)", 1, 0, set(3, 4, 5)); + execute("INSERT INTO %s (k1, k2, v) VALUES (?, ?, ?)", 1, 1, set(4, 5, 6)); + + beforeAndAfterFlush(() -> { + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE k2 > ?", 0); + + assertRows(execute("SELECT * FROM %s WHERE k2 > ? ALLOW FILTERING", 0), + row(0, 1, set(2, 3, 4)), + row(1, 1, set(4, 5, 6))); + + assertRows(execute("SELECT * FROM %s WHERE k2 >= ? AND v CONTAINS ? ALLOW FILTERING", 1, 6), + row(1, 1, set(4, 5, 6))); + + assertEmpty(execute("SELECT * FROM %s WHERE k2 < ? AND v CONTAINS ? ALLOW FILTERING", 0, 7)); + }); + } + private ResultMessage.Prepared prepareStatement(String cql, boolean forThrift) { return QueryProcessor.prepare(String.format(cql, KEYSPACE, currentTable()), 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 19f798f2f1..b91650dadc 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java @@ -958,11 +958,14 @@ public class SelectMultiColumnRelationTest extends CQLTester row(0, 0, 1, 1, 0, 4), row(0, 0, 1, 1, 1, 5)); - assertInvalidMessage("Partition key parts: b must be restricted as other parts are", - "SELECT * FROM %s WHERE a = ? AND (c, d) IN ((?, ?), (?, ?)) ALLOW FILTERING", 0, 1, 1, 2, 1); + assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c, d) IN ((?, ?)) ALLOW FILTERING", 0, 1, 1), + row(0, 0, 1, 1, 0, 4), + row(0, 0, 1, 1, 1, 5)); - assertInvalidMessage("Partition key parts: b must be restricted as other parts are", - "SELECT * FROM %s WHERE a = ? AND (c, d) >= (?, ?) ALLOW FILTERING", 0, 1, 1); + assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c, d) >= (?, ?) ALLOW FILTERING", 0, 1, 1), + row(0, 0, 1, 1, 0, 4), + row(0, 0, 1, 1, 1, 5), + row(0, 0, 2, 0, 0, 5)); assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND (c) IN ((?)) AND f = ?", 0, 0, 1, 5), row(0, 0, 1, 1, 1, 5)); diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderedPartitionerTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderedPartitionerTest.java index 83e7e4738c..0e3a342364 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderedPartitionerTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderedPartitionerTest.java @@ -27,8 +27,11 @@ import org.junit.Test; import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; + import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.cql3.restrictions.StatementRestrictions; import org.apache.cassandra.dht.ByteOrderedPartitioner; /** @@ -42,6 +45,33 @@ public class SelectOrderedPartitionerTest extends CQLTester DatabaseDescriptor.setPartitionerUnsafe(ByteOrderedPartitioner.instance); } + @Test + public void testFilteringOnPartitionKeyWithToken() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY ((a, b), c))"); + + for (int i = 0; i < 10; i++) + { + execute("INSERT INTO %s (a,b,c,d) VALUES (?, ?, ?, ?)", i, i, i, i); + execute("INSERT INTO %s (a,b,c,d) VALUES (?, ?, ?, ?)", i, i + 10, i + 10, i + 10); + } + + beforeAndAfterFlush(() -> { + assertRowsIgnoringOrder(execute("SELECT * FROM %s WHERE token(a, b) > token(5, 10) AND b < 8 ALLOW FILTERING"), + row(6, 6, 6, 6), + row(7, 7, 7, 7)); + + assertRows(execute("SELECT * FROM %s WHERE token(a, b) > token(8, 10) AND a = 9 ALLOW FILTERING"), + row(9, 9, 9, 9), + row(9, 19, 19, 19)); + + assertRows(execute("SELECT * FROM %s WHERE token(a, b) > token(8, 10) AND a = 9 AND c = 19 ALLOW FILTERING"), + row(9, 19, 19, 19)); + + assertEmpty(execute("SELECT * FROM %s WHERE token(a, b) = token(8, 8) AND b = 9 ALLOW FILTERING")); + }); + } + @Test public void testTokenFunctionWithSingleColumnPartitionKey() throws Throwable { @@ -130,7 +160,7 @@ public class SelectOrderedPartitionerTest extends CQLTester assertRows(execute("SELECT * FROM %s WHERE token(a) < token(?) AND token(a) >= token(?) AND a IN (?, ?);", 1, 3, 1, 3), row(3, 3)); - assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)", + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "SELECT * FROM %s WHERE token(a) > token(?) AND token(a) <= token(?) AND a > ?;", 1, 3, 1); assertRows(execute("SELECT * FROM %s WHERE token(a) > token(?) AND token(a) <= token(?) AND a IN ?;", @@ -206,7 +236,7 @@ public class SelectOrderedPartitionerTest extends CQLTester assertEmpty(execute("SELECT * FROM %s WHERE b IN (?, ?) AND token(a, b) = token(?, ?) AND a = ?;", 0, 1, 0, 0, 1)); - assertInvalidMessage("Partition key parts: b must be restricted as other parts are", + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "SELECT * FROM %s WHERE token(a, b) > token(?, ?) AND a = ?;", 0, 0, 1); } diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java index a52ce66d19..2ad0427339 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java @@ -206,9 +206,9 @@ public class SelectSingleColumnRelationTest extends CQLTester row("second", 1, 1, 1), row("second", 4, 4, 4)); - assertInvalidMessage("Partition key parts: b must be restricted as other parts are", + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "select * from %s where a in (?, ?)", "first", "second"); - assertInvalidMessage("Partition key parts: b must be restricted as other parts are", + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "select * from %s where a = ?", "first"); assertInvalidMessage("b cannot be restricted by more than one relation if it includes a IN", "select * from %s where a = ? AND b IN (?, ?) AND b = ?", "first", 2, 2, 3); @@ -498,11 +498,14 @@ public class SelectSingleColumnRelationTest extends CQLTester row(0, 0, 1, 1, 0, 4), row(0, 0, 1, 1, 1, 5)); - assertInvalidMessage("Partition key parts: b must be restricted as other parts are", - "SELECT * FROM %s WHERE a = ? AND c IN (?, ?) AND d IN (?) ALLOW FILTERING", 0, 0, 1, 1); + assertRows(execute("SELECT * FROM %s WHERE a = ? AND c IN (?) AND d IN (?) ALLOW FILTERING", 0, 1, 1), + row(0, 0, 1, 1, 0, 4), + row(0, 0, 1, 1, 1, 5)); - assertInvalidMessage("Partition key parts: b must be restricted as other parts are", - "SELECT * FROM %s WHERE a = ? AND (c, d) >= (?, ?) ALLOW FILTERING", 0, 1, 1); + assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c, d) >= (?, ?) ALLOW FILTERING", 0, 1, 1), + row(0, 0, 1, 1, 0, 4), + row(0, 0, 1, 1, 1, 5), + row(0, 0, 2, 0, 0, 5)); assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "SELECT * FROM %s WHERE a = ? AND c IN (?, ?) AND f = ?", 0, 0, 1, 5); @@ -520,8 +523,11 @@ public class SelectSingleColumnRelationTest extends CQLTester assertRows(execute("SELECT * FROM %s WHERE a = ? AND c IN (?, ?) AND d IN (?) AND f = ? ALLOW FILTERING", 0, 1, 3, 0, 3), row(0, 0, 1, 0, 0, 3)); - assertInvalidMessage("Partition key parts: b must be restricted as other parts are", - "SELECT * FROM %s WHERE a = ? AND c >= ? ALLOW FILTERING", 0, 1); + assertRows(execute("SELECT * FROM %s WHERE a = ? AND c >= ? ALLOW FILTERING", 0, 1), + row(0, 0, 1, 0, 0, 3), + row(0, 0, 1, 1, 0, 4), + row(0, 0, 1, 1, 1, 5), + row(0, 0, 2, 0, 0, 5)); assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "SELECT * FROM %s WHERE a = ? AND c >= ? AND f = ?", 0, 1, 5); @@ -592,7 +598,7 @@ public class SelectSingleColumnRelationTest extends CQLTester public void testInvalidSliceRestrictionOnPartitionKey() throws Throwable { createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c text)"); - assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)", + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, "SELECT * FROM %s WHERE a >= 1 and a < 4"); assertInvalidMessage("Multi-column relations can only be applied to clustering columns but was applied to: a", "SELECT * FROM %s WHERE (a) >= (1) and (a) < (4)"); @@ -604,9 +610,9 @@ public class SelectSingleColumnRelationTest extends CQLTester createTable("CREATE TABLE %s (a int, b int, c text, PRIMARY KEY ((a, b)))"); assertInvalidMessage("Multi-column relations can only be applied to clustering columns but was applied to: a", "SELECT * FROM %s WHERE (a, b) >= (1, 1) and (a, b) < (4, 1)"); - assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)", + assertInvalidMessage("Multi-column relations can only be applied to clustering columns but was applied to: a", "SELECT * FROM %s WHERE a >= 1 and (a, b) < (4, 1)"); - assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)", + assertInvalidMessage("Multi-column relations can only be applied to clustering columns but was applied to: a", "SELECT * FROM %s WHERE b >= 1 and (a, b) < (4, 1)"); assertInvalidMessage("Multi-column relations can only be applied to clustering columns but was applied to: a", "SELECT * FROM %s WHERE (a, b) >= (1, 1) and (b) < (4)"); 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 db0a4cdbf2..7d56a146f1 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java @@ -1412,6 +1412,33 @@ public class SelectTest extends CQLTester } + @Test + public void testAllowFilteringOnPartitionKeyOnStaticColumnsWithRowsWithOnlyStaticValues() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, s int static, c int, d int, primary key (a, b))"); + + for (int i = 0; i < 5; i++) + { + execute("INSERT INTO %s (a, s) VALUES (?, ?)", i, i); + if (i != 2) + for (int j = 0; j < 4; j++) + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", i, j, j, i + j); + } + + assertRowsIgnoringOrder(execute("SELECT * FROM %s WHERE a >= 1 AND c = 2 AND s >= 1 ALLOW FILTERING"), + row(1, 2, 1, 2, 3), + row(3, 2, 3, 2, 5), + row(4, 2, 4, 2, 6)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND c = 2 AND s >= 1 LIMIT 2 ALLOW FILTERING"), + row(1, 2, 1, 2, 3), + row(4, 2, 4, 2, 6)); + + assertRowsIgnoringOrder(execute("SELECT * FROM %s WHERE a >= 3 AND c = 2 AND s >= 1 LIMIT 2 ALLOW FILTERING"), + row(4, 2, 4, 2, 6), + row(3, 2, 3, 2, 5)); + } + @Test public void testFilteringOnStaticColumnsWithRowsWithOnlyStaticValues() throws Throwable { @@ -2283,6 +2310,1084 @@ public class SelectTest extends CQLTester execute("SELECT * FROM %s WHERE a = 'foo' AND b = ?", new String(TOO_BIG.array())); } + @Test + public void testAllowFilteringOnPartitionKeyWithDistinct() throws Throwable + { + // Test a regular(CQL3) table. + createTable("CREATE TABLE %s (pk0 int, pk1 int, ck0 int, val int, PRIMARY KEY((pk0, pk1), ck0))"); + + for (int i = 0; i < 3; i++) + { + execute("INSERT INTO %s (pk0, pk1, ck0, val) VALUES (?, ?, 0, 0)", i, i); + execute("INSERT INTO %s (pk0, pk1, ck0, val) VALUES (?, ?, 1, 1)", i, i); + } + + beforeAndAfterFlush(() -> { + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT DISTINCT pk0, pk1 FROM %s WHERE pk1 = 1 LIMIT 3"); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT DISTINCT pk0, pk1 FROM %s WHERE pk0 > 0 AND pk1 = 1 LIMIT 3"); + + assertRows(execute("SELECT DISTINCT pk0, pk1 FROM %s WHERE pk0 = 1 LIMIT 1 ALLOW FILTERING"), + row(1, 1)); + + assertRows(execute("SELECT DISTINCT pk0, pk1 FROM %s WHERE pk1 = 1 LIMIT 3 ALLOW FILTERING"), + row(1, 1)); + + assertEmpty(execute("SELECT DISTINCT pk0, pk1 FROM %s WHERE pk0 < 0 AND pk1 = 1 LIMIT 3 ALLOW FILTERING")); + + // Test selection validation. + assertInvalidMessage("queries must request all the partition key columns", + "SELECT DISTINCT pk0 FROM %s ALLOW FILTERING"); + assertInvalidMessage("queries must only request partition key columns", + "SELECT DISTINCT pk0, pk1, ck0 FROM %s ALLOW FILTERING"); + }); + + // Test a 'compact storage' table. + createTable("CREATE TABLE %s (pk0 int, pk1 int, val int, PRIMARY KEY((pk0, pk1))) WITH COMPACT STORAGE"); + + for (int i = 0; i < 3; i++) + execute("INSERT INTO %s (pk0, pk1, val) VALUES (?, ?, ?)", i, i, i); + + beforeAndAfterFlush(() -> { + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT DISTINCT pk0, pk1 FROM %s WHERE pk1 = 1 LIMIT 3"); + + assertRows(execute("SELECT DISTINCT pk0, pk1 FROM %s WHERE pk0 < 2 AND pk1 = 1 LIMIT 1 ALLOW FILTERING"), + row(1, 1)); + + assertRows(execute("SELECT DISTINCT pk0, pk1 FROM %s WHERE pk1 > 1 LIMIT 3 ALLOW FILTERING"), + row(2, 2)); + }); + + // Test a 'wide row' thrift table. + createTable("CREATE TABLE %s (pk int, name text, val int, PRIMARY KEY(pk, name)) WITH COMPACT STORAGE"); + + for (int i = 0; i < 3; i++) + { + execute("INSERT INTO %s (pk, name, val) VALUES (?, 'name0', 0)", i); + execute("INSERT INTO %s (pk, name, val) VALUES (?, 'name1', 1)", i); + } + + beforeAndAfterFlush(() -> { + assertRows(execute("SELECT DISTINCT pk FROM %s WHERE pk > 1 LIMIT 1 ALLOW FILTERING"), + row(2)); + + assertRows(execute("SELECT DISTINCT pk FROM %s WHERE pk > 0 LIMIT 3 ALLOW FILTERING"), + row(1), + row(2)); + }); + } + + @Test + public void testAllowFilteringOnPartitionKey() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY ((a, b), c))"); + + execute("INSERT INTO %s (a,b,c,d) VALUES (11, 12, 13, 14)"); + execute("INSERT INTO %s (a,b,c,d) VALUES (11, 15, 16, 17)"); + execute("INSERT INTO %s (a,b,c,d) VALUES (21, 22, 23, 24)"); + execute("INSERT INTO %s (a,b,c,d) VALUES (31, 32, 33, 34)"); + + beforeAndAfterFlush(() -> { + + assertInvalidMessage("IN restrictions are not supported when the query involves filtering", + "SELECT * FROM %s WHERE b in (11,12) ALLOW FILTERING"); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a = 11"); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 11"); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 11 and b = 1"); + + assertRows(execute("SELECT * FROM %s WHERE a = 11 ALLOW FILTERING"), + row(11, 12, 13, 14), + row(11, 15, 16, 17)); + + assertRows(execute("SELECT * FROM %s WHERE a in (11) and b in (12,15,22)"), + row(11, 12, 13, 14), + row(11, 15, 16, 17)); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE b in (12,15,22)"); + + assertRows(execute("SELECT * FROM %s WHERE a in (11) and b in (12,15,22) ALLOW FILTERING"), + row(11, 12, 13, 14), + row(11, 15, 16, 17)); + + assertRows(execute("SELECT * FROM %s WHERE a in (11) ALLOW FILTERING"), + row(11, 12, 13, 14), + row(11, 15, 16, 17)); + + assertRows(execute("SELECT * FROM %s WHERE b = 15 ALLOW FILTERING"), + row(11, 15, 16, 17)); + + assertRows(execute("SELECT * FROM %s WHERE b >= 15 ALLOW FILTERING"), + row(11, 15, 16, 17), + row(31, 32, 33, 34), + row(21, 22, 23, 24)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 11 ALLOW FILTERING"), + row(11, 12, 13, 14), + row(11, 15, 16, 17), + row(31, 32, 33, 34), + row(21, 22, 23, 24)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 11 AND b <= 15 ALLOW FILTERING"), + row(11, 12, 13, 14), + row(11, 15, 16, 17)); + + assertRows(execute("SELECT * FROM %s WHERE a <= 11 AND b >= 14 ALLOW FILTERING"), + row(11, 15, 16, 17)); + + Object[][] res = getRows(execute("SELECT * FROM %s WHERE a < 11 ALLOW FILTERING")); + assertEquals(0, res.length); + res = getRows(execute("SELECT * FROM %s WHERE b > 32 ALLOW FILTERING")); + assertEquals(0, res.length); + }); + + // Checks filtering with unset + assertInvalidMessage("Unsupported unset value for column a", + "SELECT * FROM %s WHERE a = ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column a", + "SELECT * FROM %s WHERE a > ? ALLOW FILTERING", + unset()); + + // No clustering key + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY ((a, b)))"); + + execute("INSERT INTO %s (a,b,c,d) VALUES (11, 12, 13, 14)"); + execute("INSERT INTO %s (a,b,c,d) VALUES (11, 15, 16, 17)"); + execute("INSERT INTO %s (a,b,c,d) VALUES (21, 22, 23, 24)"); + execute("INSERT INTO %s (a,b,c,d) VALUES (31, 32, 33, 34)"); + + beforeAndAfterFlush(() -> { + + assertInvalidMessage("IN restrictions are not supported when the query involves filtering", + "SELECT * FROM %s WHERE b in (11,12) ALLOW FILTERING"); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a = 11"); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 11"); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 11 and b = 1"); + + assertRows(execute("SELECT * FROM %s WHERE a = 11 ALLOW FILTERING"), + row(11, 12, 13, 14), + row(11, 15, 16, 17)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 11 ALLOW FILTERING"), + row(11, 12, 13, 14), + row(11, 15, 16, 17), + row(31, 32, 33, 34), + row(21, 22, 23, 24)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 11 AND b <= 15 ALLOW FILTERING"), + row(11, 12, 13, 14), + row(11, 15, 16, 17)); + + assertRows(execute("SELECT * FROM %s WHERE a <= 11 AND b >= 14 ALLOW FILTERING"), + row(11, 15, 16, 17)); + }); + + // ---------------------------------------------- + // one partition key + // ---------------------------------------------- + createTable("CREATE TABLE %s (a int primary key, b int, c int)"); + + execute("INSERT INTO %s (a, b, c) VALUES (1, 2, 4)"); + execute("INSERT INTO %s (a, b, c) VALUES (2, 1, 6)"); + execute("INSERT INTO %s (a, b, c) VALUES (3, 2, 4)"); + execute("INSERT INTO %s (a, b, c) VALUES (4, 1, 7)"); + + // Adds tomstones + execute("INSERT INTO %s (a, b, c) VALUES (0, 1, 4)"); + execute("INSERT INTO %s (a, b, c) VALUES (5, 2, 7)"); + execute("DELETE FROM %s WHERE a = 0"); + execute("DELETE FROM %s WHERE a = 5"); + + beforeAndAfterFlush(() -> { + assertRows(execute("SELECT * FROM %s WHERE a = 1 ALLOW FILTERING"), + row(1, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 ALLOW FILTERING"), + row(1, 2, 4), + row(2, 1, 6), + row(4, 1, 7), + row(3, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b >= 2 ALLOW FILTERING"), + row(1, 2, 4), + row(3, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b >= 2 AND c <= 4 ALLOW FILTERING"), + row(1, 2, 4), + row(3, 2, 4)); + }); + } + + @Test + public void testAllowFilteringOnPartitionAndClusteringKey() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, PRIMARY KEY ((a, b), c, d))"); + + execute("INSERT INTO %s (a,b,c,d,e) VALUES (11, 12, 13, 14, 15)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (11, 15, 16, 17, 18)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (21, 22, 23, 24, 25)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (31, 32, 33, 34, 35)"); + + beforeAndAfterFlush(() -> { + + assertRows(execute("SELECT * FROM %s WHERE a = 11 AND b = 15 AND c = 16"), + row(11, 15, 16, 17, 18)); + + assertInvalidMessage( + "Clustering column \"d\" cannot be restricted (preceding column \"c\" is restricted by a non-EQ relation)", + "SELECT * FROM %s WHERE a = 11 AND b = 12 AND c > 13 AND d = 14"); + + assertRows(execute("SELECT * FROM %s WHERE a = 11 AND b = 15 AND c = 16 AND d > 16"), + row(11, 15, 16, 17, 18)); + + assertRows(execute("SELECT * FROM %s WHERE a = 11 AND b = 15 AND c > 13 AND d >= 17 ALLOW FILTERING"), + row(11, 15, 16, 17, 18)); + assertInvalidMessage( + "Clustering column \"d\" cannot be restricted (preceding column \"c\" is restricted by a non-EQ relation)", + "SELECT * FROM %s WHERE a = 11 AND b = 12 AND c > 13 AND d > 17"); + + assertRows(execute("SELECT * FROM %s WHERE c > 30 AND d >= 34 ALLOW FILTERING"), + row(31, 32, 33, 34, 35)); + + assertRows(execute("SELECT * FROM %s WHERE a <= 11 AND c > 15 AND d >= 16 ALLOW FILTERING"), + row(11, 15, 16, 17, 18)); + + assertRows(execute("SELECT * FROM %s WHERE a <= 11 AND b >= 15 AND c > 15 AND d >= 16 ALLOW FILTERING"), + row(11, 15, 16, 17, 18)); + + assertRows(execute("SELECT * FROM %s WHERE a <= 100 AND b >= 15 AND c > 0 AND d <= 100 ALLOW FILTERING"), + row(11, 15, 16, 17, 18), + row(31, 32, 33, 34, 35), + row(21, 22, 23, 24, 25)); + + assertInvalidMessage( + "Clustering column \"d\" cannot be restricted (preceding column \"c\" is restricted by a non-EQ relation)", + "SELECT * FROM %s WHERE a <= 11 AND c > 15 AND d >= 16"); + }); + + // test clutering order + createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, PRIMARY KEY ((a, b), c, d)) WITH CLUSTERING ORDER BY (c DESC)"); + + execute("INSERT INTO %s (a,b,c,d,e) VALUES (11, 11, 13, 14, 15)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (11, 11, 14, 17, 18)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (11, 12, 15, 14, 15)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (11, 12, 16, 17, 18)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (21, 11, 23, 24, 25)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (21, 11, 24, 34, 35)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (21, 12, 25, 24, 25)"); + execute("INSERT INTO %s (a,b,c,d,e) VALUES (21, 12, 26, 34, 35)"); + + beforeAndAfterFlush(() -> { + assertRowsIgnoringOrder(execute("SELECT * FROM %s WHERE b >= 12 ALLOW FILTERING"), + row(11, 12, 15, 14, 15), + row(11, 12, 16, 17, 18), + row(21, 12, 25, 24, 25), + row(21, 12, 26, 34, 35)); + }); + } + + @Test + public void testAllowFilteringOnPartitionKeyWithoutIndicesWithCollections() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c list, d set, e map, PRIMARY KEY ((a, b)))"); + + execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 2, [1, 6], {2, 12}, {1: 6})"); + execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 3, [3, 2], {6, 4}, {3: 2})"); + execute("INSERT INTO %s (a, b, c, d, e) VALUES (1, 4, [1, 2], {2, 4}, {1: 2})"); + execute("INSERT INTO %s (a, b, c, d, e) VALUES (2, 3, [3, 6], {6, 12}, {3: 6})"); + + beforeAndAfterFlush(() -> { + + // Checks filtering for lists + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE b < 0 AND c CONTAINS 2"); + + assertRows(execute("SELECT * FROM %s WHERE b >= 4 AND c CONTAINS 2 ALLOW FILTERING"), + row(1, 4, list(1, 2), set(2, 4), map(1, 2))); + + assertRows( + execute("SELECT * FROM %s WHERE a > 0 AND b <= 3 AND c CONTAINS 2 AND c CONTAINS 3 ALLOW FILTERING"), + row(1, 3, list(3, 2), set(6, 4), map(3, 2))); + + // Checks filtering for sets + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a = 1 AND d CONTAINS 4"); + + assertRows(execute("SELECT * FROM %s WHERE d CONTAINS 4 ALLOW FILTERING"), + row(1, 3, list(3, 2), set(6, 4), map(3, 2)), + row(1, 4, list(1, 2), set(2, 4), map(1, 2))); + + assertRows(execute("SELECT * FROM %s WHERE d CONTAINS 4 AND d CONTAINS 6 ALLOW FILTERING"), + row(1, 3, list(3, 2), set(6, 4), map(3, 2))); + + // Checks filtering for maps + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE e CONTAINS 2"); + + assertRows(execute("SELECT * FROM %s WHERE a < 2 AND b >= 3 AND e CONTAINS 2 ALLOW FILTERING"), + row(1, 3, list(3, 2), set(6, 4), map(3, 2)), + row(1, 4, list(1, 2), set(2, 4), map(1, 2))); + + assertRows(execute("SELECT * FROM %s WHERE a = 1 AND e CONTAINS KEY 1 ALLOW FILTERING"), + row(1, 4, list(1, 2), set(2, 4), map(1, 2)), + row(1, 2, list(1, 6), set(2, 12), map(1, 6))); + + assertRows(execute("SELECT * FROM %s WHERE a in (1) AND b in (2) AND e[1] = 6 ALLOW FILTERING"), + row(1, 2, list(1, 6), set(2, 12), map(1, 6))); + + assertRows(execute("SELECT * FROM %s WHERE a = 1 AND e CONTAINS KEY 1 AND e CONTAINS 2 ALLOW FILTERING"), + row(1, 4, list(1, 2), set(2, 4), map(1, 2))); + + assertRows( + execute("SELECT * FROM %s WHERE a >= 1 AND b in (3) AND c CONTAINS 2 AND d CONTAINS 4 AND e CONTAINS KEY 3 ALLOW FILTERING"), + row(1, 3, list(3, 2), set(6, 4), map(3, 2))); + }); + + // Checks filtering with null + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a > 1 AND c CONTAINS null ALLOW FILTERING"); + assertInvalidMessage("Unsupported null value for column d", + "SELECT * FROM %s WHERE b < 1 AND d CONTAINS null ALLOW FILTERING"); + assertInvalidMessage("Unsupported null value for column e", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND e CONTAINS null ALLOW FILTERING"); + assertInvalidMessage("Unsupported null value for column e", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND e CONTAINS KEY null ALLOW FILTERING"); + assertInvalidMessage("Unsupported null map key for column e", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND e[null] = 2 ALLOW FILTERING"); + assertInvalidMessage("Unsupported null map value for column e", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND e[1] = null ALLOW FILTERING"); + + // Checks filtering with unset + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND c CONTAINS ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column d", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND d CONTAINS ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column e", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND e CONTAINS ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column e", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND e CONTAINS KEY ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset map key for column e", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND e[?] = 2 ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset map value for column e", + "SELECT * FROM %s WHERE a >= 1 AND b < 1 AND e[1] = ? ALLOW FILTERING", + unset()); + } + + @Test + public void testAllowFilteringOnPartitionKeyWithCounters() throws Throwable + { + for (String compactStorageClause : new String[] { "", " WITH COMPACT STORAGE" }) + { + createTable("CREATE TABLE %s (a int, b int, c int, cnt counter, PRIMARY KEY ((a, b), c))" + + compactStorageClause); + + execute("UPDATE %s SET cnt = cnt + ? WHERE a = ? AND b = ? AND c = ?", 14L, 11, 12, 13); + execute("UPDATE %s SET cnt = cnt + ? WHERE a = ? AND b = ? AND c = ?", 24L, 21, 22, 23); + execute("UPDATE %s SET cnt = cnt + ? WHERE a = ? AND b = ? AND c = ?", 27L, 21, 22, 26); + execute("UPDATE %s SET cnt = cnt + ? WHERE a = ? AND b = ? AND c = ?", 34L, 31, 32, 33); + execute("UPDATE %s SET cnt = cnt + ? WHERE a = ? AND b = ? AND c = ?", 24L, 41, 42, 43); + + beforeAndAfterFlush(() -> { + + assertRows(executeFilteringOnly("SELECT * FROM %s WHERE cnt = 24"), + row(41, 42, 43, 24L), + row(21, 22, 23, 24L)); + assertRows(executeFilteringOnly("SELECT * FROM %s WHERE b > 22 AND cnt = 24"), + row(41, 42, 43, 24L)); + assertRows(executeFilteringOnly("SELECT * FROM %s WHERE b > 10 AND b < 25 AND cnt = 24"), + row(21, 22, 23, 24L)); + assertRows(executeFilteringOnly("SELECT * FROM %s WHERE b > 10 AND c < 25 AND cnt = 24"), + row(21, 22, 23, 24L)); + + assertInvalidMessage( + "ORDER BY is only supported when the partition key is restricted by an EQ or an IN.", + "SELECT * FROM %s WHERE a = 21 AND b > 10 AND cnt > 23 ORDER BY c DESC ALLOW FILTERING"); + + assertRows(executeFilteringOnly("SELECT * FROM %s WHERE a = 21 AND b = 22 AND cnt > 23 ORDER BY c DESC"), + row(21, 22, 26, 27L), + row(21, 22, 23, 24L)); + + assertRows(executeFilteringOnly("SELECT * FROM %s WHERE cnt > 20 AND cnt < 30"), + row(41, 42, 43, 24L), + row(21, 22, 23, 24L), + row(21, 22, 26, 27L)); + }); + } + } + + @Test + public void testAllowFilteringOnPartitionKeyOnCompactTablesWithoutIndicesAndWithLists() throws Throwable + { + // ---------------------------------------------- + // Test COMPACT table with clustering columns + // ---------------------------------------------- + createTable("CREATE TABLE %s (a int, b int, c frozen>, PRIMARY KEY (a, b)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (a, b, c) VALUES (1, 2, [4, 2])"); + execute("INSERT INTO %s (a, b, c) VALUES (1, 3, [6, 2])"); + execute("INSERT INTO %s (a, b, c) VALUES (1, 4, [4, 1])"); + execute("INSERT INTO %s (a, b, c) VALUES (2, 3, [7, 1])"); + + beforeAndAfterFlush(() -> { + + // Checks filtering + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND b = 4 AND c = [4, 1]"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b >= 4 AND c = [4, 1] ALLOW FILTERING"), + row(1, 4, list(4, 1))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 0 AND c > [4, 2]"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND c > [4, 2] ALLOW FILTERING"), + row(1, 3, list(6, 2)), + row(2, 3, list(7, 1))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND b <= 3 AND c < [6, 2]"); + + assertRows(execute("SELECT * FROM %s WHERE a <= 1 AND b <= 3 AND c < [6, 2] ALLOW FILTERING"), + row(1, 2, list(4, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a <= 1 AND c >= [4, 2] AND c <= [6, 4]"); + + assertRows(execute("SELECT * FROM %s WHERE a > 0 AND b <= 3 AND c >= [4, 2] AND c <= [6, 4] ALLOW FILTERING"), + row(1, 2, list(4, 2)), + row(1, 3, list(6, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c CONTAINS 2"); + + assertRows(execute("SELECT * FROM %s WHERE a > 0 AND c CONTAINS 2 ALLOW FILTERING"), + row(1, 2, list(4, 2)), + row(1, 3, list(6, 2))); + + assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c", + "SELECT * FROM %s WHERE a > 1 AND c CONTAINS KEY 2 ALLOW FILTERING"); + + assertRows(execute("SELECT * FROM %s WHERE a < 2 AND c CONTAINS 2 AND c CONTAINS 6 ALLOW FILTERING"), + row(1, 3, list(6, 2))); + }); + // Checks filtering with null + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c = null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a > 1 AND c = null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c > null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a > 1 AND c > null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c CONTAINS null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a > 1 AND c CONTAINS null ALLOW FILTERING"); + + // Checks filtering with unset + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a > 1 AND c = ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a > 1 AND c > ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a > 1 AND c CONTAINS ? ALLOW FILTERING", + unset()); + + // ---------------------------------------------- + // Test COMPACT table without clustering columns + // ---------------------------------------------- + createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c frozen>) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (a, b, c) VALUES (1, 2, [4, 2])"); + execute("INSERT INTO %s (a, b, c) VALUES (2, 1, [6, 2])"); + execute("INSERT INTO %s (a, b, c) VALUES (3, 2, [4, 1])"); + execute("INSERT INTO %s (a, b, c) VALUES (4, 1, [7, 1])"); + + beforeAndAfterFlush(() -> { + + // Checks filtering + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND b = 2 AND c = [4, 2]"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b = 2 AND c = [4, 2] ALLOW FILTERING"), + row(1, 2, list(4, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c > [4, 2]"); + + assertRows(execute("SELECT * FROM %s WHERE a > 3 AND c > [4, 2] ALLOW FILTERING"), + row(4, 1, list(7, 1))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a < 1 AND b < 3 AND c <= [4, 2]"); + + assertRows(execute("SELECT * FROM %s WHERE a < 3 AND b < 3 AND c <= [4, 2] ALLOW FILTERING"), + row(1, 2, list(4, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c >= [4, 3] AND c <= [7]"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 2 AND c >= [4, 3] AND c <= [7] ALLOW FILTERING"), + row(2, 1, list(6, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 3 AND c CONTAINS 2"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND c CONTAINS 2 ALLOW FILTERING"), + row(1, 2, list(4, 2)), + row(2, 1, list(6, 2))); + + assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c", + "SELECT * FROM %s WHERE a >=1 AND c CONTAINS KEY 2 ALLOW FILTERING"); + + assertRows(execute("SELECT * FROM %s WHERE a < 3 AND c CONTAINS 2 AND c CONTAINS 6 ALLOW FILTERING"), + row(2, 1, list(6, 2))); + }); + + // Checks filtering with null + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c = null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a > 1 AND c = null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c > null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a > 1 AND c > null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c CONTAINS null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a > 1 AND c CONTAINS null ALLOW FILTERING"); + + // Checks filtering with unset + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a > 1 AND c = ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a > 1 AND c > ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a > 1 AND c CONTAINS ? ALLOW FILTERING", + unset()); + } + + + @Test + public void testAllowFilteringOnPartitionKeyOnCompactTablesWithoutIndicesAndWithMaps() throws Throwable + { + //---------------------------------------------- + // Test COMPACT table with clustering columns + //---------------------------------------------- + createTable("CREATE TABLE %s (a int, b int, c frozen>, PRIMARY KEY (a, b)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4 : 2})"); + execute("INSERT INTO %s (a, b, c) VALUES (1, 3, {6 : 2})"); + execute("INSERT INTO %s (a, b, c) VALUES (1, 4, {4 : 1})"); + execute("INSERT INTO %s (a, b, c) VALUES (2, 3, {7 : 1})"); + + beforeAndAfterFlush(() -> { + + // Checks filtering + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND b = 4 AND c = {4 : 1}"); + + assertRows(execute("SELECT * FROM %s WHERE a <= 1 AND b = 4 AND c = {4 : 1} ALLOW FILTERING"), + row(1, 4, map(4, 1))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c > {4 : 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a > 1 AND c > {4 : 2} ALLOW FILTERING"), + row(2, 3, map(7, 1))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND b <= 3 AND c < {6 : 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b <= 3 AND c < {6 : 2} ALLOW FILTERING"), + row(1, 2, map(4, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 1 AND c >= {4 : 2} AND c <= {6 : 4}"); + + assertRows(execute("SELECT * FROM %s WHERE a > 0 AND c >= {4 : 2} AND c <= {6 : 4} ALLOW FILTERING"), + row(1, 2, map(4, 2)), + row(1, 3, map(6, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a > 10 AND c CONTAINS 2"); + + assertRows(execute("SELECT * FROM %s WHERE a > 0 AND c CONTAINS 2 ALLOW FILTERING"), + row(1, 2, map(4, 2)), + row(1, 3, map(6, 2))); + + assertRows(execute("SELECT * FROM %s WHERE a < 2 AND c CONTAINS KEY 6 ALLOW FILTERING"), + row(1, 3, map(6, 2))); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND c CONTAINS 2 AND c CONTAINS KEY 6 ALLOW FILTERING"), + row(1, 3, map(6, 2))); + }); + + // Checks filtering with null + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c = null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c = null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c > null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c > null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS null ALLOW FILTERING"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS KEY null ALLOW FILTERING"); + + // Checks filtering with unset + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c = ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c > ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS KEY ? ALLOW FILTERING", + unset()); + + //---------------------------------------------- + // Test COMPACT table without clustering columns + //---------------------------------------------- + createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c frozen>) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4 : 2})"); + execute("INSERT INTO %s (a, b, c) VALUES (2, 1, {6 : 2})"); + execute("INSERT INTO %s (a, b, c) VALUES (3, 2, {4 : 1})"); + execute("INSERT INTO %s (a, b, c) VALUES (4, 1, {7 : 1})"); + + beforeAndAfterFlush(() -> { + + // Checks filtering + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND b = 2 AND c = {4 : 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b = 2 AND c = {4 : 2} ALLOW FILTERING"), + row(1, 2, map(4, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c > {4 : 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND c > {4 : 2} ALLOW FILTERING"), + row(2, 1, map(6, 2)), + row(4, 1, map(7, 1))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND b < 3 AND c <= {4 : 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b < 3 AND c <= {4 : 2} ALLOW FILTERING"), + row(1, 2, map(4, 2)), + row(3, 2, map(4, 1))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c >= {4 : 3} AND c <= {7 : 1}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 2 AND c >= {5 : 2} AND c <= {7 : 0} ALLOW FILTERING"), + row(2, 1, map(6, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS 2"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND c CONTAINS 2 ALLOW FILTERING"), + row(1, 2, map(4, 2)), + row(2, 1, map(6, 2))); + + assertRows(execute("SELECT * FROM %s WHERE a > 0 AND c CONTAINS KEY 4 ALLOW FILTERING"), + row(1, 2, map(4, 2)), + row(3, 2, map(4, 1))); + + assertRows(execute("SELECT * FROM %s WHERE a >= 2 AND c CONTAINS 2 AND c CONTAINS KEY 6 ALLOW FILTERING"), + row(2, 1, map(6, 2))); + }); + + // Checks filtering with null + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c = null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c = null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c > null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c > null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS KEY null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS KEY null ALLOW FILTERING"); + + // Checks filtering with unset + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c = ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c > ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS KEY ? ALLOW FILTERING", + unset()); + } + + @Test + public void testAllowFilteringOnPartitionKeyOnCompactTablesWithoutIndicesAndWithSets() throws Throwable + { + //---------------------------------------------- + // Test COMPACT table with clustering columns + //---------------------------------------------- + createTable("CREATE TABLE %s (a int, b int, c frozen>, PRIMARY KEY (a, b)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4, 2})"); + execute("INSERT INTO %s (a, b, c) VALUES (1, 3, {6, 2})"); + execute("INSERT INTO %s (a, b, c) VALUES (1, 4, {4, 1})"); + execute("INSERT INTO %s (a, b, c) VALUES (2, 3, {7, 1})"); + + beforeAndAfterFlush(() -> { + + // Checks filtering + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND b = 4 AND c = {4, 1}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b = 4 AND c = {4, 1} ALLOW FILTERING"), + row(1, 4, set(4, 1))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c > {4, 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND c > {4, 2} ALLOW FILTERING"), + row(1, 3, set(6, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND b <= 3 AND c < {6, 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a > 0 AND b <= 3 AND c < {6, 2} ALLOW FILTERING"), + row(1, 2, set(2, 4)), + row(2, 3, set(1, 7))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c >= {4, 2} AND c <= {6, 4}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 0 AND c >= {4, 2} AND c <= {6, 4} ALLOW FILTERING"), + row(1, 2, set(4, 2)), + row(1, 3, set(6, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS 2"); + + assertRows(execute("SELECT * FROM %s WHERE a < 2 AND c CONTAINS 2 ALLOW FILTERING"), + row(1, 2, set(4, 2)), + row(1, 3, set(6, 2))); + + assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS KEY 2 ALLOW FILTERING"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND c CONTAINS 2 AND c CONTAINS 6 ALLOW FILTERING"), + row(1, 3, set(6, 2))); + }); + // Checks filtering with null + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c = null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c = null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c > null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c > null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS null ALLOW FILTERING"); + + // Checks filtering with unset + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c = ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c > ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS ? ALLOW FILTERING", + unset()); + + //---------------------------------------------- + // Test COMPACT table without clustering columns + //---------------------------------------------- + createTable("CREATE TABLE %s (a int PRIMARY KEY, b int, c frozen>) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (a, b, c) VALUES (1, 2, {4, 2})"); + execute("INSERT INTO %s (a, b, c) VALUES (2, 1, {6, 2})"); + execute("INSERT INTO %s (a, b, c) VALUES (3, 2, {4, 1})"); + execute("INSERT INTO %s (a, b, c) VALUES (4, 1, {7, 1})"); + + beforeAndAfterFlush(() -> { + + // Checks filtering + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND b = 2 AND c = {4, 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b = 2 AND c = {4, 2} ALLOW FILTERING"), + row(1, 2, set(4, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c > {4, 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND c > {4, 2} ALLOW FILTERING"), + row(2, 1, set(6, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND b < 3 AND c <= {4, 2}"); + + assertRows(execute("SELECT * FROM %s WHERE a <= 4 AND b < 3 AND c <= {4, 2} ALLOW FILTERING"), + row(1, 2, set(4, 2)), + row(4, 1, set(1, 7)), + row(3, 2, set(4, 1))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c >= {4, 3} AND c <= {7}"); + + assertRows(execute("SELECT * FROM %s WHERE a < 3 AND c >= {5, 2} AND c <= {7} ALLOW FILTERING"), + row(2, 1, set(6, 2))); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS 2"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 0 AND c CONTAINS 2 ALLOW FILTERING"), + row(1, 2, set(4, 2)), + row(2, 1, set(6, 2))); + + assertInvalidMessage("Cannot use CONTAINS KEY on non-map column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS KEY 2 ALLOW FILTERING"); + + assertRows(execute("SELECT * FROM %s WHERE a >= 2 AND c CONTAINS 2 AND c CONTAINS 6 ALLOW FILTERING"), + row(2, 1, set(6, 2))); + }); + + // Checks filtering with null + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c = null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c = null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c > null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c > null ALLOW FILTERING"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS null"); + assertInvalidMessage("Unsupported null value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS null ALLOW FILTERING"); + + // Checks filtering with unset + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c = ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c > ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column c", + "SELECT * FROM %s WHERE a >= 1 AND c CONTAINS ? ALLOW FILTERING", + unset()); + } + + @Test + public void testAllowFilteringOnPartitionKeyOnCompactTablesWithoutIndices() throws Throwable + { + // ---------------------------------------------- + // Test COMPACT table with clustering columns + // ---------------------------------------------- + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY ((a, b), c)) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (a, b, c, d) VALUES (1, 2, 4, 5)"); + execute("INSERT INTO %s (a, b, c, d) VALUES (1, 3, 6, 7)"); + execute("INSERT INTO %s (a, b, c, d) VALUES (1, 4, 4, 5)"); + execute("INSERT INTO %s (a, b, c, d) VALUES (2, 3, 7, 8)"); + + // Adds tomstones + execute("INSERT INTO %s (a, b, c, d) VALUES (1, 1, 4, 5)"); + execute("INSERT INTO %s (a, b, c, d) VALUES (2, 2, 7, 8)"); + execute("DELETE FROM %s WHERE a = 1 AND b = 1 AND c = 4"); + execute("DELETE FROM %s WHERE a = 2 AND b = 2 AND c = 7"); + + beforeAndAfterFlush(() -> { + assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = 4"), + row(1, 4, 4, 5)); + + // Checks filtering + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = 4 AND d = 5"); + + assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = 4 ALLOW FILTERING"), + row(1, 4, 4, 5)); + + assertInvalidMessage("IN predicates on non-primary-key columns (d) is not yet supported", + "SELECT * FROM %s WHERE a IN (1, 2) AND b = 3 AND d IN (6, 7)"); + + assertInvalidMessage("IN predicates on non-primary-key columns (d) is not yet supported", + "SELECT * FROM %s WHERE a IN (1, 2) AND b = 3 AND d IN (6, 7) ALLOW FILTERING"); + + assertRows(execute("SELECT * FROM %s WHERE a < 2 AND c > 4 AND c <= 6 ALLOW FILTERING"), + row(1, 3, 6, 7)); + + assertRows(execute("SELECT * FROM %s WHERE a <= 1 AND b >= 2 AND c >= 4 AND d <= 8 ALLOW FILTERING"), + row(1, 3, 6, 7), + row(1, 4, 4, 5), + row(1, 2, 4, 5)); + + assertRows(execute("SELECT * FROM %s WHERE a = 1 AND c >= 4 AND d <= 8 ALLOW FILTERING"), + row(1, 3, 6, 7), + row(1, 4, 4, 5), + row(1, 2, 4, 5)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 2 AND c >= 4 AND d <= 8 ALLOW FILTERING"), + row(2, 3, 7, 8)); + }); + + // Checks filtering with null + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE d = null"); + assertInvalidMessage("Unsupported null value for column a", + "SELECT * FROM %s WHERE a = null ALLOW FILTERING"); + assertInvalidMessage("Unsupported null value for column a", + "SELECT * FROM %s WHERE a > null ALLOW FILTERING"); + + // Checks filtering with unset + assertInvalidMessage("Unsupported unset value for column a", + "SELECT * FROM %s WHERE a = ? ALLOW FILTERING", + unset()); + assertInvalidMessage("Unsupported unset value for column a", + "SELECT * FROM %s WHERE a > ? ALLOW FILTERING", + unset()); + + //---------------------------------------------- + // Test COMPACT table without clustering columns + //---------------------------------------------- + createTable("CREATE TABLE %s (a int primary key, b int, c int) WITH COMPACT STORAGE"); + + execute("INSERT INTO %s (a, b, c) VALUES (1, 2, 4)"); + execute("INSERT INTO %s (a, b, c) VALUES (2, 1, 6)"); + execute("INSERT INTO %s (a, b, c) VALUES (3, 2, 4)"); + execute("INSERT INTO %s (a, b, c) VALUES (4, 1, 7)"); + + // Adds tomstones + execute("INSERT INTO %s (a, b, c) VALUES (0, 1, 4)"); + execute("INSERT INTO %s (a, b, c) VALUES (5, 2, 7)"); + execute("DELETE FROM %s WHERE a = 0"); + execute("DELETE FROM %s WHERE a = 5"); + + beforeAndAfterFlush(() -> { + + // Checks filtering + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE a = 1 AND b = 4 AND c = 4"); + + assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 AND c = 4 ALLOW FILTERING"), + row(1, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE a = 1 AND b = 2 ALLOW FILTERING"), + row(1, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE b >= 2 AND c <= 4 ALLOW FILTERING"), + row(1, 2, 4), + row(3, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE a = 1 ALLOW FILTERING"), + row(1, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE b >= 2 ALLOW FILTERING"), + row(1, 2, 4), + row(3, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 2 AND b <=1 ALLOW FILTERING"), + row(2, 1, 6), + row(4, 1, 7)); + + assertRows(execute("SELECT * FROM %s WHERE a = 1 AND c >= 4 ALLOW FILTERING"), + row(1, 2, 4)); + + assertInvalidMessage("IN predicates on non-primary-key columns (b) is not yet supported", + "SELECT * FROM %s WHERE a = 1 AND b IN (1, 2) AND c IN (6, 7)"); + + assertInvalidMessage("IN predicates on non-primary-key columns (c) is not yet supported", + "SELECT * FROM %s WHERE a IN (1, 2) AND c IN (6, 7) ALLOW FILTERING"); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE c > 4"); + + assertRows(execute("SELECT * FROM %s WHERE c > 4 ALLOW FILTERING"), + row(2, 1, 6), + row(4, 1, 7)); + + assertRows(execute("SELECT * FROM %s WHERE a >= 1 AND b >= 2 AND c <= 4 ALLOW FILTERING"), + row(1, 2, 4), + row(3, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE a < 3 AND c <= 4 ALLOW FILTERING"), + row(1, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE a < 3 AND b >= 2 AND c <= 4 ALLOW FILTERING"), + row(1, 2, 4)); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE c >= 3 AND c <= 6"); + + assertRows(execute("SELECT * FROM %s WHERE c <=6 ALLOW FILTERING"), + row(1, 2, 4), + row(2, 1, 6), + row(3, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE token(a) >= token(2)"), + row(2, 1, 6), + row(4, 1, 7), + row(3, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE token(a) >= token(2) ALLOW FILTERING"), + row(2, 1, 6), + row(4, 1, 7), + row(3, 2, 4)); + + assertRows(execute("SELECT * FROM %s WHERE token(a) >= token(2) AND b = 1 ALLOW FILTERING"), + row(2, 1, 6), + row(4, 1, 7)); + + }); + } + @Test public void testFilteringOnCompactTablesWithoutIndicesAndWithMaps() throws Throwable { @@ -2981,8 +4086,8 @@ public class SelectTest extends CQLTester 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"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE p1 = 1 AND v = 3"); createIndex("CREATE INDEX ON %s(v)"); execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 1, 3); diff --git a/test/unit/org/apache/cassandra/index/internal/CassandraIndexTest.java b/test/unit/org/apache/cassandra/index/internal/CassandraIndexTest.java index 067109aa04..e4f5379ed6 100644 --- a/test/unit/org/apache/cassandra/index/internal/CassandraIndexTest.java +++ b/test/unit/org/apache/cassandra/index/internal/CassandraIndexTest.java @@ -112,7 +112,7 @@ public class CassandraIndexTest extends CQLTester .indexName("k1_index") .withFirstRow(row(0, 0, 0, 0, 0)) .withSecondRow(row(1, 1, 1, 1, 1)) - .missingIndexMessage("Partition key parts: k2 must be restricted as other parts are") + .missingIndexMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE) .firstQueryExpression("k1=0") .secondQueryExpression("k1=1") .run(); @@ -127,7 +127,7 @@ public class CassandraIndexTest extends CQLTester .indexName("k2_index") .withFirstRow(row(0, 0, 0, 0, 0)) .withSecondRow(row(1, 1, 1, 1, 1)) - .missingIndexMessage("Partition key parts: k1 must be restricted as other parts are") + .missingIndexMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE) .firstQueryExpression("k2=0") .secondQueryExpression("k2=1") .run();