You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2016/07/04 12:45:13 UTC

[4/6] cassandra git commit: Merge branch cassandra-3.0 into cassandra-3.9

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 <b....@gmail.com>
Authored: Mon Jul 4 14:33:20 2016 +0200
Committer: Benjamin Lerer <b....@gmail.com>
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));
+     }
  }