Merge branch cassandra-3.0 into cassandra-3.9
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/118f1a0d Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/118f1a0d Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/118f1a0d Branch: refs/heads/trunk Commit: 118f1a0d698eddedd70bcd72193bf3796219b9a7 Parents: c86b3e1 9244531 Author: Benjamin Lerer <[email protected]> Authored: Mon Jul 4 14:33:20 2016 +0200 Committer: Benjamin Lerer <[email protected]> Committed: Mon Jul 4 14:34:32 2016 +0200 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../ClusteringColumnRestrictions.java | 20 ++------ .../restrictions/MultiColumnRestriction.java | 2 +- .../SelectMultiColumnRelationTest.java | 27 ++++++++++ .../cql3/validation/operations/SelectTest.java | 54 ++++++++++++++++++++ 5 files changed, 86 insertions(+), 18 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/118f1a0d/CHANGES.txt ---------------------------------------------------------------------- diff --cc CHANGES.txt index 475365f,2df77e1..1a03b89 --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -5,34 -4,11 +5,35 @@@ Merged from 3.0 Merged from 2.2: * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973) Merged from 2.1: + * Fix filtering on clustering columns when 2i is used (CASSANDRA-11907) + * Avoid stalling paxos when the paxos state expires (CASSANDRA-12043) + * Remove finished incoming streaming connections from MessagingService (CASSANDRA-11854) -3.0.8 - * Fix potential race in schema during new table creation (CASSANDRA-12083) +3.8 + * Improve details in compaction log message (CASSANDRA-12080) + * Allow unset values in CQLSSTableWriter (CASSANDRA-11911) + * Chunk cache to request compressor-compatible buffers if pool space is exhausted (CASSANDRA-11993) + * Remove DatabaseDescriptor dependencies from SequentialWriter (CASSANDRA-11579) + * Move skip_stop_words filter before stemming (CASSANDRA-12078) + * Support seek() in EncryptedFileSegmentInputStream (CASSANDRA-11957) + * SSTable tools mishandling LocalPartitioner (CASSANDRA-12002) + * When SEPWorker assigned work, set thread name to match pool (CASSANDRA-11966) + * Add cross-DC latency metrics (CASSANDRA-11596) + * Allow terms in selection clause (CASSANDRA-10783) + * Add bind variables to trace (CASSANDRA-11719) + * Switch counter shards' clock to timestamps (CASSANDRA-9811) + * Introduce HdrHistogram and response/service/wait separation to stress tool (CASSANDRA-11853) + * entry-weighers in QueryProcessor should respect partitionKeyBindIndexes field (CASSANDRA-11718) + * Support older ant versions (CASSANDRA-11807) + * Estimate compressed on disk size when deciding if sstable size limit reached (CASSANDRA-11623) + * cassandra-stress profiles should support case sensitive schemas (CASSANDRA-11546) + * Remove DatabaseDescriptor dependency from FileUtils (CASSANDRA-11578) + * Faster streaming (CASSANDRA-9766) + * Add prepared query parameter to trace for "Execute CQL3 prepared query" session (CASSANDRA-11425) + * Add repaired percentage metric (CASSANDRA-11503) + * Add Change-Data-Capture (CASSANDRA-8844) +Merged from 3.0: * cqlsh: fix error handling in rare COPY FROM failure scenario (CASSANDRA-12070) * Disable autocompaction during drain (CASSANDRA-11878) * Add a metrics timer to MemtablePool and use it to track time spent blocked on memory in MemtableAllocator (CASSANDRA-11327) http://git-wip-us.apache.org/repos/asf/cassandra/blob/118f1a0d/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java index 837ee13,0000000..dc349d9 mode 100644,000000..100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java @@@ -1,229 -1,0 +1,215 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.cql3.restrictions; + +import java.util.*; + +import org.apache.cassandra.config.CFMetaData; +import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.statements.Bound; +import org.apache.cassandra.db.*; +import org.apache.cassandra.db.filter.RowFilter; +import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.index.SecondaryIndexManager; +import org.apache.cassandra.utils.btree.BTreeSet; + +import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse; +import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest; + +/** + * A set of restrictions on the clustering key. + */ +final class ClusteringColumnRestrictions extends RestrictionSetWrapper +{ + /** + * The composite type. + */ + protected final ClusteringComparator comparator; + + /** + * <code>true</code> if filtering is allowed for this restriction, <code>false</code> otherwise + */ + private final boolean allowFiltering; + + public ClusteringColumnRestrictions(CFMetaData cfm) + { + this(cfm, false); + } + + public ClusteringColumnRestrictions(CFMetaData cfm, boolean allowFiltering) + { + this(cfm.comparator, new RestrictionSet(), allowFiltering); + } + + private ClusteringColumnRestrictions(ClusteringComparator comparator, + RestrictionSet restrictionSet, + boolean allowFiltering) + { + super(restrictionSet); + this.comparator = comparator; + this.allowFiltering = allowFiltering; + } + + public ClusteringColumnRestrictions mergeWith(Restriction restriction) throws InvalidRequestException + { + SingleRestriction newRestriction = (SingleRestriction) restriction; + RestrictionSet newRestrictionSet = restrictions.addRestriction(newRestriction); + + if (!isEmpty() && !allowFiltering) + { + SingleRestriction lastRestriction = restrictions.lastRestriction(); + ColumnDefinition lastRestrictionStart = lastRestriction.getFirstColumn(); + ColumnDefinition newRestrictionStart = restriction.getFirstColumn(); + + checkFalse(lastRestriction.isSlice() && newRestrictionStart.position() > lastRestrictionStart.position(), + "Clustering column \"%s\" cannot be restricted (preceding column \"%s\" is restricted by a non-EQ relation)", + newRestrictionStart.name, + lastRestrictionStart.name); + + if (newRestrictionStart.position() < lastRestrictionStart.position() && newRestriction.isSlice()) + throw invalidRequest("PRIMARY KEY column \"%s\" cannot be restricted (preceding column \"%s\" is restricted by a non-EQ relation)", + restrictions.nextColumn(newRestrictionStart).name, + newRestrictionStart.name); + } + + return new ClusteringColumnRestrictions(this.comparator, newRestrictionSet, allowFiltering); + } + + private boolean hasMultiColumnSlice() + { + for (SingleRestriction restriction : restrictions) + { + if (restriction.isMultiColumn() && restriction.isSlice()) + return true; + } + return false; + } + + public NavigableSet<Clustering> valuesAsClustering(QueryOptions options) throws InvalidRequestException + { + MultiCBuilder builder = MultiCBuilder.create(comparator, hasIN()); + for (SingleRestriction r : restrictions) + { + r.appendTo(builder, options); + if (builder.hasMissingElements()) + break; + } + return builder.build(); + } + + public NavigableSet<ClusteringBound> boundsAsClustering(Bound bound, QueryOptions options) throws InvalidRequestException + { + MultiCBuilder builder = MultiCBuilder.create(comparator, hasIN() || hasMultiColumnSlice()); + int keyPosition = 0; + + for (SingleRestriction r : restrictions) + { + if (handleInFilter(r, keyPosition)) + break; + + if (r.isSlice()) + { + r.appendBoundTo(builder, bound, options); + return builder.buildBoundForSlice(bound.isStart(), + r.isInclusive(bound), + r.isInclusive(bound.reverse()), + r.getColumnDefs()); + } + + r.appendBoundTo(builder, bound, options); + + if (builder.hasMissingElements()) + return BTreeSet.empty(comparator); + + keyPosition = r.getLastColumn().position() + 1; + } + + // Everything was an equal (or there was nothing) + return builder.buildBound(bound.isStart(), true); + } + + /** + * Checks if any of the underlying restriction is a CONTAINS or CONTAINS KEY. + * + * @return <code>true</code> if any of the underlying restriction is a CONTAINS or CONTAINS KEY, + * <code>false</code> otherwise + */ + public final boolean hasContains() + { + return restrictions.stream().anyMatch(SingleRestriction::isContains); + } + + /** + * Checks if any of the underlying restriction is a slice restrictions. + * + * @return <code>true</code> if any of the underlying restriction is a slice restrictions, + * <code>false</code> otherwise + */ + public final boolean hasSlice() + { + return restrictions.stream().anyMatch(SingleRestriction::isSlice); + } + + /** + * Checks if underlying restrictions would require filtering + * + * @return <code>true</code> if any underlying restrictions require filtering, <code>false</code> + * otherwise + */ + public final boolean needFiltering() + { + int position = 0; - SingleRestriction slice = null; ++ + for (SingleRestriction restriction : restrictions) + { + if (handleInFilter(restriction, position)) + return true; + - if (slice != null && !slice.getFirstColumn().equals(restriction.getFirstColumn())) - return true; - - if (slice == null && restriction.isSlice()) - slice = restriction; - else ++ if (!restriction.isSlice()) + position = restriction.getLastColumn().position() + 1; + } + return hasContains(); + } + + @Override + public void addRowFilterTo(RowFilter filter, + SecondaryIndexManager indexManager, + QueryOptions options) throws InvalidRequestException + { + int position = 0; + - SingleRestriction slice = null; + for (SingleRestriction restriction : restrictions) + { + // We ignore all the clustering columns that can be handled by slices. + if (handleInFilter(restriction, position) || restriction.hasSupportingIndex(indexManager)) + { + restriction.addRowFilterTo(filter, indexManager, options); + continue; + } + - if (slice != null && !slice.getFirstColumn().equals(restriction.getFirstColumn())) - { - restriction.addRowFilterTo(filter, indexManager, options); - continue; - } - - if (slice == null && restriction.isSlice()) - slice = restriction; - else ++ if (!restriction.isSlice()) + position = restriction.getLastColumn().position() + 1; + } + } + + private boolean handleInFilter(SingleRestriction restriction, int index) { + return restriction.isContains() || restriction.isLIKE() || index != restriction.getFirstColumn().position(); + } + +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/118f1a0d/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java index 012b319,9d33bb1..e5e3bc8 --- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java @@@ -474,9 -475,9 +474,9 @@@ public abstract class MultiColumnRestri @Override public final void addRowFilterTo(RowFilter filter, SecondaryIndexManager indexManager, - QueryOptions options) throws InvalidRequestException + QueryOptions options) { - throw invalidRequest("Slice restrictions are not supported on indexed columns"); + throw invalidRequest("Multi-column slice restrictions cannot be used for filtering."); } @Override http://git-wip-us.apache.org/repos/asf/cassandra/blob/118f1a0d/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java ---------------------------------------------------------------------- diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java index 1239b7a,ce74fe2..7f43c6b --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java @@@ -885,19 -885,46 +885,46 @@@ public class SelectMultiColumnRelationT } @Test + public void testMultipleClusteringWithIndexAndValueOver64K() throws Throwable + { + createTable("CREATE TABLE %s (a int, b blob, c int, d int, PRIMARY KEY (a, b, c))"); + createIndex("CREATE INDEX ON %s (b)"); + + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, ByteBufferUtil.bytes(1), 0, 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, ByteBufferUtil.bytes(2), 1, 0); + + assertInvalidMessage("Index expression values may not be larger than 64K", + "SELECT * FROM %s WHERE (b, c) = (?, ?) AND d = ? ALLOW FILTERING", TOO_BIG, 1, 2); + } + + @Test + public void testMultiColumnRestrictionsWithIndex() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, v int, PRIMARY KEY (a, b, c, d, e))"); + createIndex("CREATE INDEX ON %s (v)"); + for (int i = 1; i <= 5; i++) + { + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, 0, 0, 0, 0); + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, i, 0, 0, 0); + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, i, i, 0, 0); + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, i, i, i, 0); + execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, i, i, i, i, i); + } + + String errorMsg = "Multi-column slice restrictions cannot be used for filtering."; + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND (c,d) < (2,2) AND v = 0 ALLOW FILTERING"); + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND (d,e) < (2,2) AND b = 1 AND v = 0 ALLOW FILTERING"); + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND b = 1 AND (d,e) < (2,2) AND v = 0 ALLOW FILTERING"); + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND b > 1 AND (d,e) < (2,2) AND v = 0 ALLOW FILTERING"); + assertInvalidMessage(errorMsg, + "SELECT * FROM %s WHERE a = 0 AND (b,c) > (1,0) AND (d,e) < (2,2) AND v = 0 ALLOW FILTERING"); + } + + @Test - public void testMultipleClusteringWithIndexAndValueOver64K() throws Throwable - { - createTable("CREATE TABLE %s (a int, b blob, c int, d int, PRIMARY KEY (a, b, c))"); - createIndex("CREATE INDEX ON %s (b)"); - - execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, ByteBufferUtil.bytes(1), 0, 0); - execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, ByteBufferUtil.bytes(2), 1, 0); - - assertInvalidMessage("Index expression values may not be larger than 64K", - "SELECT * FROM %s WHERE (b, c) = (?, ?) AND d = ? ALLOW FILTERING", TOO_BIG, 1, 2); - } - - @Test public void testMultiplePartitionKeyAndMultiClusteringWithIndex() throws Throwable { createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, f int, PRIMARY KEY ((a, b), c, d, e))"); http://git-wip-us.apache.org/repos/asf/cassandra/blob/118f1a0d/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java ---------------------------------------------------------------------- diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java index dde87d8,1b6fe9b..9a1493b --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java @@@ -2937,4 -2533,47 +2937,58 @@@ public class SelectTest extends CQLTest row("a", 3, 5)); } } + + @Test + public void testFilteringWithSecondaryIndex() throws Throwable + { + createTable("CREATE TABLE %s (pk int, " + + "c1 int, " + + "c2 int, " + + "c3 int, " + + "v int, " + + "PRIMARY KEY (pk, c1, c2, c3))"); + createIndex("CREATE INDEX v_idx_1 ON %s (v);"); + + for (int i = 1; i <= 5; i++) + { + execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 1, 1, i); + execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, ?)", 1, 1, 1, i, i); + execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, ?)", 1, 1, i, i, i); + execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, ?)", 1, i, i, i, i); + } + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 > 0 AND c1 < 5 AND c2 = 1 AND v = 3 ALLOW FILTERING;"), + row(1, 1, 1, 3, 3)); + + assertEmpty(execute("SELECT * FROM %s WHERE pk = 1 AND c1 > 1 AND c1 < 5 AND c2 = 1 AND v = 3 ALLOW FILTERING;")); + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 > 1 AND c2 > 2 AND c3 > 2 AND v = 3 ALLOW FILTERING;"), + row(1, 3, 3, 3, 3)); + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 > 1 AND c2 > 2 AND c3 = 3 AND v = 3 ALLOW FILTERING;"), + row(1, 3, 3, 3, 3)); + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 IN(0,1,2) AND c2 = 1 AND v = 3 ALLOW FILTERING;"), + row(1, 1, 1, 3, 3)); + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND c1 IN(0,1,2) AND c2 = 1 AND v = 3"), + row(1, 1, 1, 3, 3)); ++ } + - assertInvalidMessage("Clustering column \"c2\" cannot be restricted (preceding column \"c1\" is restricted by a non-EQ relation)", - "SELECT * FROM %s WHERE pk = 1 AND c1 > 0 AND c1 < 5 AND c2 = 1 ALLOW FILTERING;"); ++ @Test ++ public void testIndexQueryWithCompositePartitionKey() throws Throwable ++ { ++ createTable("CREATE TABLE %s (p1 int, p2 int, v int, PRIMARY KEY ((p1, p2)))"); ++ assertInvalidMessage("Partition key parts: p2 must be restricted as other parts are", ++ "SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING"); ++ createIndex("CREATE INDEX ON %s(v)"); + - assertInvalidMessage("PRIMARY KEY column \"c2\" cannot be restricted as preceding column \"c1\" is not restricted", - "SELECT * FROM %s WHERE pk = 1 AND c2 = 1 ALLOW FILTERING;"); ++ execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 1, 3); ++ execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 2, 3); ++ execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 2, 1, 3); ++ ++ assertRows(execute("SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW FILTERING"), ++ row(1, 2, 3), ++ row(1, 1, 3)); + } }
