You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ty...@apache.org on 2015/02/10 22:09:57 UTC

[1/2] cassandra git commit: Fix multicolumn relations with indexes on some clustering cols

Repository: cassandra
Updated Branches:
  refs/heads/cassandra-2.1 ad91d4162 -> 07ffe1b12


Fix multicolumn relations with indexes on some clustering cols

Patch by Benjamin Lerer; reviewed by Tyler Hobbs for CASSANDRA-8275


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/9649594c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/9649594c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/9649594c

Branch: refs/heads/cassandra-2.1
Commit: 9649594c761dbb72e58ddd71a10f0794378337ca
Parents: 28c380c
Author: blerer <b_...@hotmail.com>
Authored: Tue Feb 10 15:07:02 2015 -0600
Committer: Tyler Hobbs <ty...@apache.org>
Committed: Tue Feb 10 15:07:02 2015 -0600

----------------------------------------------------------------------
 CHANGES.txt                                     |   2 +
 .../cql3/statements/SelectStatement.java        |  46 ++++--
 .../cassandra/cql3/MultiColumnRelationTest.java | 122 ++++++++++++++++
 .../cql3/SingleColumnRelationTest.java          | 145 +++++++++++++++++++
 4 files changed, 303 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/9649594c/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index fa9c77d..861730f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,6 @@
 2.0.13:
+ * Fix some multi-column relations with indexes on some clustering
+   columns (CASSANDRA-8275)
  * Fix IllegalArgumentException in dynamic snitch (CASSANDRA-8448)
  * Add support for UPDATE ... IF EXISTS (CASSANDRA-8610)
  * Fix reversal of list prepends (CASSANDRA-8733)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9649594c/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 19615b6..2fa57b9 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -35,7 +35,6 @@ import org.apache.cassandra.cql3.CFDefinition.Name.Kind;
 import org.apache.cassandra.transport.messages.ResultMessage;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.*;
-import org.apache.cassandra.db.context.CounterContext;
 import org.apache.cassandra.db.filter.*;
 import org.apache.cassandra.db.marshal.*;
 import org.apache.cassandra.dht.*;
@@ -83,8 +82,10 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
     /** Restrictions on non-primary key columns (i.e. secondary index restrictions) */
     private final Map<CFDefinition.Name, Restriction> metadataRestrictions = new HashMap<CFDefinition.Name, Restriction>();
 
-    // The name of all restricted names not covered by the key or index filter
-    private final Set<CFDefinition.Name> restrictedNames = new HashSet<CFDefinition.Name>();
+    // The map keys are the name of the columns that must be converted into IndexExpressions if a secondary index need
+    // to be used. The value specify if the column has an index that can be used to for the relation in which the column
+    // is specified.
+    private final Map<CFDefinition.Name, Boolean> restrictedNames = new HashMap<CFDefinition.Name, Boolean>();
     private Restriction.Slice sliceRestriction;
 
     private boolean isReversed;
@@ -1027,7 +1028,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
             return Collections.emptyList();
 
         List<IndexExpression> expressions = new ArrayList<IndexExpression>();
-        for (CFDefinition.Name name : restrictedNames)
+        for (CFDefinition.Name name : restrictedNames.keySet())
         {
             Restriction restriction;
             switch (name.kind)
@@ -1068,12 +1069,21 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
             }
             else
             {
-                List<ByteBuffer> values = restriction.values(variables);
+                ByteBuffer value;
+                if (restriction.isMultiColumn())
+                {
+                    List<ByteBuffer> values = restriction.values(variables);
+                    value = values.get(name.position);
+                }
+                else
+                {
+                    List<ByteBuffer> values = restriction.values(variables);
+                    if (values.size() != 1)
+                        throw new InvalidRequestException("IN restrictions are not supported on indexed columns");
 
-                if (values.size() != 1)
-                    throw new InvalidRequestException("IN restrictions are not supported on indexed columns");
+                    value = values.get(0);
+                }
 
-                ByteBuffer value = values.get(0);
                 validateIndexExpressionValue(value, name);
                 expressions.add(new IndexExpression(name.name.key, IndexOperator.EQ, value));
             }
@@ -1496,7 +1506,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
             // All (or none) of the partition key columns have been specified;
             // hence there is no need to turn these restrictions into index expressions.
             if (!stmt.usesSecondaryIndexing)
-                stmt.restrictedNames.removeAll(cfDef.partitionKeys());
+                stmt.restrictedNames.keySet().removeAll(cfDef.partitionKeys());
 
             if (stmt.selectsOnlyStaticColumns && stmt.hasClusteringColumnsRestriction())
                 throw new InvalidRequestException("Cannot restrict clustering columns when selecting only static columns");
@@ -1507,8 +1517,17 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
             if (stmt.isKeyRange && hasQueriableClusteringColumnIndex)
                 stmt.usesSecondaryIndexing = true;
 
-            if (!stmt.usesSecondaryIndexing)
-                stmt.restrictedNames.removeAll(cfDef.clusteringColumns());
+            // The clustering columns that can be used to perform a slice filtering on the secondary index do not
+            // need to be converted into IndexExpressions. Therefore, if they are not indexed by an index that support
+            // the relation in which they have been specified, we can removes them from the restrictedNames map.
+            for (Name clusteringColumn : cfDef.clusteringColumns())
+            {
+                Boolean indexed = stmt.restrictedNames.get(clusteringColumn);
+                if (indexed == null)
+                    break;
+                if (!indexed)
+                    stmt.restrictedNames.remove(clusteringColumn);
+            }
 
             // Even if usesSecondaryIndexing is false at this point, we'll still have to use one if
             // there is restrictions not covered by the PK.
@@ -1540,9 +1559,12 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
             if (name == null)
                 handleUnrecognizedEntity(entity, relation);
 
-            stmt.restrictedNames.add(name);
             if (cfDef.cfm.getColumnDefinition(name.name.key).isIndexed() && relation.operator() == Relation.Type.EQ)
+            {
+                stmt.restrictedNames.put(name, Boolean.TRUE);
                 return new boolean[]{true, name.kind == CFDefinition.Name.Kind.COLUMN_ALIAS};
+            }
+            stmt.restrictedNames.put(name, Boolean.FALSE);
             return new boolean[]{false, false};
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9649594c/test/unit/org/apache/cassandra/cql3/MultiColumnRelationTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/MultiColumnRelationTest.java b/test/unit/org/apache/cassandra/cql3/MultiColumnRelationTest.java
index ea4f1a6..30a9226 100644
--- a/test/unit/org/apache/cassandra/cql3/MultiColumnRelationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/MultiColumnRelationTest.java
@@ -76,6 +76,15 @@ public class MultiColumnRelationTest
                     "CREATE TABLE IF NOT EXISTS %s.multiple_clustering_reversed" + tableSuffix +
                         "(a int, b int, c int, d int, PRIMARY KEY (a, b, c, d)) WITH " + compactOption + " CLUSTERING ORDER BY (b DESC, c ASC, d DESC)");
         }
+
+        executeSchemaChange("CREATE TABLE IF NOT EXISTS %s.multiple_clustering_with_indices (a int, b int, c int, d int, e int, PRIMARY KEY (a, b, c, d))");
+        executeSchemaChange("CREATE INDEX ON %s.multiple_clustering_with_indices (b)");
+        executeSchemaChange("CREATE INDEX ON %s.multiple_clustering_with_indices (e)");
+
+        executeSchemaChange("CREATE TABLE IF NOT EXISTS %s.partition_with_indices (a int, b int, c int, d int, e int, f int, PRIMARY KEY ((a, b), c, d, e))");
+        executeSchemaChange("CREATE INDEX ON %s.partition_with_indices (c)");
+        executeSchemaChange("CREATE INDEX ON %s.partition_with_indices (f)");
+
         clientState = ClientState.forInternalCalls();
     }
 
@@ -1178,6 +1187,119 @@ public class MultiColumnRelationTest
         }
     }
 
+    @Test
+    public void testMultipleClusteringWithIndex() throws Throwable
+    {
+        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 0, 0, 0, 0)");
+        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 0, 1, 0, 1)");
+        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 0, 1, 1, 2)");
+        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 1, 0, 0, 0)");
+        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 1, 1, 0, 1)");
+        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 1, 1, 1, 2)");
+        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 2, 0, 0, 0)");
+
+        UntypedResultSet results = execute("SELECT * FROM %s.multiple_clustering_with_indices WHERE (b) = (1)");
+        assertEquals(3, results.size());
+        checkRow(0, results, 0, 1, 0, 0, 0);
+        checkRow(1, results, 0, 1, 1, 0, 1);
+        checkRow(2, results, 0, 1, 1, 1, 2);
+
+        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) = (1, 1) ALLOW FILTERING");
+        assertEquals(2, results.size());
+        checkRow(0, results, 0, 1, 1, 0, 1);
+        checkRow(1, results, 0, 1, 1, 1, 2);
+
+        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) = (1, 1) AND e = 2 ALLOW FILTERING");
+        assertEquals(1, results.size());
+        checkRow(0, results, 0, 1, 1, 1, 2);
+
+        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b) IN ((1)) AND e = 2 ALLOW FILTERING");
+        assertEquals(1, results.size());
+        checkRow(0, results, 0, 1, 1, 1, 2);
+
+        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b) IN ((0), (1)) AND e = 2 ALLOW FILTERING");
+        assertEquals(2, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 2);
+        checkRow(1, results, 0, 1, 1, 1, 2);
+
+        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) IN ((0, 1)) AND e = 2 ALLOW FILTERING");
+        assertEquals(1, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 2);
+
+        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) IN ((0, 1), (1, 1)) AND e = 2 ALLOW FILTERING");
+        assertEquals(2, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 2);
+        checkRow(1, results, 0, 1, 1, 1, 2);
+
+        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b) >= (1) AND e = 2 ALLOW FILTERING");
+        assertEquals(1, results.size());
+        checkRow(0, results, 0, 1, 1, 1, 2);
+
+        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) >= (1, 1) AND e = 2 ALLOW FILTERING");
+        assertEquals(1, results.size());
+        checkRow(0, results, 0, 1, 1, 1, 2);
+    }
+
+    @Test
+    public void testPartitionWithIndex() throws Throwable
+    {
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 0, 0, 0)");
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 1, 0, 1)");
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 1, 1, 2)");
+
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 0, 0, 3)");
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 1, 0, 4)");
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 1, 1, 5)");
+
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 2, 0, 0, 5)");
+
+        UntypedResultSet results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c) = (1) ALLOW FILTERING");
+        assertEquals(3, results.size());
+        checkRow(0, results, 0, 0, 1, 0, 0, 3);
+        checkRow(1, results, 0, 0, 1, 1, 0, 4);
+        checkRow(2, results, 0, 0, 1, 1, 1, 5);
+
+        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c, d) = (1, 1) ALLOW FILTERING");
+        assertEquals(2, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 0, 4);
+        checkRow(1, results, 0, 0, 1, 1, 1, 5);
+
+        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0  AND (c) IN ((1)) AND f = 5 ALLOW FILTERING");
+        assertEquals(1, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 1, 5);
+
+        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c) IN ((1), (2)) AND f = 5 ALLOW FILTERING");
+        assertEquals(2, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 1, 5);
+        checkRow(1, results, 0, 0, 2, 0, 0, 5);
+
+        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0  AND (c, d) IN ((1, 0)) AND f = 3 ALLOW FILTERING");
+        assertEquals(1, results.size());
+        checkRow(0, results, 0, 0, 1, 0, 0, 3);
+
+        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c) >= (1) AND f = 5 ALLOW FILTERING");
+        assertEquals(2, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 1, 5);
+        checkRow(1, results, 0, 0, 2, 0, 0, 5);
+
+        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c, d) >= (1, 1) AND f = 5 ALLOW FILTERING");
+        assertEquals(2, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 1, 5);
+        checkRow(1, results, 0, 0, 2, 0, 0, 5);
+    }
+
+    @Test(expected=InvalidRequestException.class)
+    public void testMissingPartitionComponentWithInRestrictionOnIndexedColumn() throws Throwable
+    {
+        execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c, d) IN ((1, 1)) ALLOW FILTERING");
+    }
+
+    @Test(expected=InvalidRequestException.class)
+    public void testMissingPartitionComponentWithSliceRestrictionOnIndexedColumn() throws Throwable
+    {
+        execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c, d) >= (1, 1) ALLOW FILTERING");
+    }
+
     @Test(expected=InvalidRequestException.class)
     public void testPrepareLiteralInWithShortTuple() throws Throwable
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9649594c/test/unit/org/apache/cassandra/cql3/SingleColumnRelationTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/SingleColumnRelationTest.java b/test/unit/org/apache/cassandra/cql3/SingleColumnRelationTest.java
new file mode 100644
index 0000000..34d3bf1
--- /dev/null
+++ b/test/unit/org/apache/cassandra/cql3/SingleColumnRelationTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.db.ConsistencyLevel;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.gms.Gossiper;
+import org.apache.cassandra.service.ClientState;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.apache.cassandra.cql3.QueryProcessor.process;
+import static org.apache.cassandra.cql3.QueryProcessor.processInternal;
+import static org.junit.Assert.assertEquals;
+
+public class SingleColumnRelationTest
+{
+    static ClientState clientState;
+    static String keyspace = "single_column_relation_test";
+
+    @BeforeClass
+    public static void setUpClass() throws Throwable
+    {
+        SchemaLoader.loadSchema();
+        executeSchemaChange("CREATE KEYSPACE IF NOT EXISTS %s WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}");
+
+        executeSchemaChange("CREATE TABLE IF NOT EXISTS %s.partition_with_indices (a int, b int, c int, d int, e int, f int, PRIMARY KEY ((a, b), c, d, e))");
+        executeSchemaChange("CREATE INDEX ON %s.partition_with_indices (c)");
+        executeSchemaChange("CREATE INDEX ON %s.partition_with_indices (f)");
+
+        clientState = ClientState.forInternalCalls();
+    }
+
+    @AfterClass
+    public static void stopGossiper()
+    {
+        Gossiper.instance.stop();
+    }
+
+    private static void executeSchemaChange(String query) throws Throwable
+    {
+        try
+        {
+            process(String.format(query, keyspace), ConsistencyLevel.ONE);
+        } catch (RuntimeException exc)
+        {
+            throw exc.getCause();
+        }
+    }
+
+    private static UntypedResultSet execute(String query) throws Throwable
+    {
+        try
+        {
+            return processInternal(String.format(query, keyspace));
+        } catch (RuntimeException exc)
+        {
+            if (exc.getCause() != null)
+                throw exc.getCause();
+            throw exc;
+        }
+    }
+
+    @Test
+    public void testPartitionWithIndex() throws Throwable
+    {
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 0, 0, 0)");
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 1, 0, 1)");
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 1, 1, 2)");
+
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 0, 0, 3)");
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 1, 0, 4)");
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 1, 1, 5)");
+
+        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 2, 0, 0, 5)");
+
+        UntypedResultSet results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c = 1 ALLOW FILTERING");
+        assertEquals(3, results.size());
+        checkRow(0, results, 0, 0, 1, 0, 0, 3);
+        checkRow(1, results, 0, 0, 1, 1, 0, 4);
+        checkRow(2, results, 0, 0, 1, 1, 1, 5);
+
+        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c = 1 AND d = 1 ALLOW FILTERING");
+        assertEquals(2, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 0, 4);
+        checkRow(1, results, 0, 0, 1, 1, 1, 5);
+
+        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c >= 1 AND f = 5 ALLOW FILTERING");
+        assertEquals(2, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 1, 5);
+        checkRow(1, results, 0, 0, 2, 0, 0, 5);
+
+        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c = 1 AND d >= 1 AND f = 5 ALLOW FILTERING");
+        assertEquals(1, results.size());
+        checkRow(0, results, 0, 0, 1, 1, 1, 5);
+    }
+
+    @Test(expected=InvalidRequestException.class)
+    public void testMissingPartitionComponentAndFileringOnTheSecondClusteringColumnWithoutAllowFiltering() throws Throwable
+    {
+        execute("SELECT * FROM %s.partition_with_indices WHERE d >= 1 AND f = 5");
+    }
+
+    @Test(expected=InvalidRequestException.class)
+    public void testMissingPartitionComponentWithSliceRestrictionOnIndexedColumn() throws Throwable
+    {
+        execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c >= 1 ALLOW FILTERING");
+    }
+
+    private static void checkRow(int rowIndex, UntypedResultSet results, Integer... expectedValues)
+    {
+        List<UntypedResultSet.Row> rows = newArrayList(results.iterator());
+        UntypedResultSet.Row row = rows.get(rowIndex);
+        Iterator<ColumnSpecification> columns = row.getColumns().iterator();
+        for (Integer expected : expectedValues)
+        {
+            String columnName = columns.next().name.toString();
+            int actual = row.getInt(columnName);
+            assertEquals(String.format("Expected value %d for column %s in row %d, but got %s", actual, columnName, rowIndex, expected),
+                         (long) expected, actual);
+        }
+    }
+}


[2/2] cassandra git commit: Merge branch 'cassandra-2.0' into cassandra-2.1

Posted by ty...@apache.org.
Merge branch 'cassandra-2.0' into cassandra-2.1


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/07ffe1b1
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/07ffe1b1
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/07ffe1b1

Branch: refs/heads/cassandra-2.1
Commit: 07ffe1b12eb68cd51fdfc8715ffa7df14381df3a
Parents: ad91d41 9649594
Author: Tyler Hobbs <ty...@apache.org>
Authored: Tue Feb 10 15:09:39 2015 -0600
Committer: Tyler Hobbs <ty...@apache.org>
Committed: Tue Feb 10 15:09:39 2015 -0600

----------------------------------------------------------------------
 CHANGES.txt                                     |  3 +
 .../cql3/statements/SelectStatement.java        | 58 ++++++------
 .../cassandra/cql3/MultiColumnRelationTest.java | 94 ++++++++++++++++++++
 .../cql3/SingleColumnRelationTest.java          | 40 +++++++++
 4 files changed, 170 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/07ffe1b1/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 92ee5d1,861730f..2113349
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,96 -1,6 +1,99 @@@
 -2.0.13:
 +2.1.4
 + * Write partition size estimates into a system table (CASSANDRA-7688)
 + * cqlsh: Fix keys() and full() collection indexes in DESCRIBE output
 +   (CASSANDRA-8154)
++Merged from 2.0:
+  * Fix some multi-column relations with indexes on some clustering
+    columns (CASSANDRA-8275)
 +
 +
 +2.1.3
 + * Upgrade libthrift to 0.9.2 (CASSANDRA-8685)
 + * Don't use the shared ref in sstableloader (CASSANDRA-8704)
 + * Purge internal prepared statements if related tables or
 +   keyspaces are dropped (CASSANDRA-8693)
 + * (cqlsh) Handle unicode BOM at start of files (CASSANDRA-8638)
 + * Stop compactions before exiting offline tools (CASSANDRA-8623)
 + * Update tools/stress/README.txt to match current behaviour (CASSANDRA-7933)
 + * Fix schema from Thrift conversion with empty metadata (CASSANDRA-8695)
 + * Safer Resource Management (CASSANDRA-7705)
 + * Make sure we compact highly overlapping cold sstables with
 +   STCS (CASSANDRA-8635)
 + * rpc_interface and listen_interface generate NPE on startup when specified
 +   interface doesn't exist (CASSANDRA-8677)
 + * Fix ArrayIndexOutOfBoundsException in nodetool cfhistograms (CASSANDRA-8514)
 + * Switch from yammer metrics for nodetool cf/proxy histograms (CASSANDRA-8662)
 + * Make sure we don't add tmplink files to the compaction
 +   strategy (CASSANDRA-8580)
 + * (cqlsh) Handle maps with blob keys (CASSANDRA-8372)
 + * (cqlsh) Handle DynamicCompositeType schemas correctly (CASSANDRA-8563)
 + * Duplicate rows returned when in clause has repeated values (CASSANDRA-6706)
 + * Add tooling to detect hot partitions (CASSANDRA-7974)
 + * Fix cassandra-stress user-mode truncation of partition generation (CASSANDRA-8608)
 + * Only stream from unrepaired sstables during inc repair (CASSANDRA-8267)
 + * Don't allow starting multiple inc repairs on the same sstables (CASSANDRA-8316)
 + * Invalidate prepared BATCH statements when related tables
 +   or keyspaces are dropped (CASSANDRA-8652)
 + * Fix missing results in secondary index queries on collections
 +   with ALLOW FILTERING (CASSANDRA-8421)
 + * Expose EstimatedHistogram metrics for range slices (CASSANDRA-8627)
 + * (cqlsh) Escape clqshrc passwords properly (CASSANDRA-8618)
 + * Fix NPE when passing wrong argument in ALTER TABLE statement (CASSANDRA-8355)
 + * Pig: Refactor and deprecate CqlStorage (CASSANDRA-8599)
 + * Don't reuse the same cleanup strategy for all sstables (CASSANDRA-8537)
 + * Fix case-sensitivity of index name on CREATE and DROP INDEX
 +   statements (CASSANDRA-8365)
 + * Better detection/logging for corruption in compressed sstables (CASSANDRA-8192)
 + * Use the correct repairedAt value when closing writer (CASSANDRA-8570)
 + * (cqlsh) Handle a schema mismatch being detected on startup (CASSANDRA-8512)
 + * Properly calculate expected write size during compaction (CASSANDRA-8532)
 + * Invalidate affected prepared statements when a table's columns
 +   are altered (CASSANDRA-7910)
 + * Stress - user defined writes should populate sequentally (CASSANDRA-8524)
 + * Fix regression in SSTableRewriter causing some rows to become unreadable 
 +   during compaction (CASSANDRA-8429)
 + * Run major compactions for repaired/unrepaired in parallel (CASSANDRA-8510)
 + * (cqlsh) Fix compression options in DESCRIBE TABLE output when compression
 +   is disabled (CASSANDRA-8288)
 + * (cqlsh) Fix DESCRIBE output after keyspaces are altered (CASSANDRA-7623)
 + * Make sure we set lastCompactedKey correctly (CASSANDRA-8463)
 + * (cqlsh) Fix output of CONSISTENCY command (CASSANDRA-8507)
 + * (cqlsh) Fixed the handling of LIST statements (CASSANDRA-8370)
 + * Make sstablescrub check leveled manifest again (CASSANDRA-8432)
 + * Check first/last keys in sstable when giving out positions (CASSANDRA-8458)
 + * Disable mmap on Windows (CASSANDRA-6993)
 + * Add missing ConsistencyLevels to cassandra-stress (CASSANDRA-8253)
 + * Add auth support to cassandra-stress (CASSANDRA-7985)
 + * Fix ArrayIndexOutOfBoundsException when generating error message
 +   for some CQL syntax errors (CASSANDRA-8455)
 + * Scale memtable slab allocation logarithmically (CASSANDRA-7882)
 + * cassandra-stress simultaneous inserts over same seed (CASSANDRA-7964)
 + * Reduce cassandra-stress sampling memory requirements (CASSANDRA-7926)
 + * Ensure memtable flush cannot expire commit log entries from its future (CASSANDRA-8383)
 + * Make read "defrag" async to reclaim memtables (CASSANDRA-8459)
 + * Remove tmplink files for offline compactions (CASSANDRA-8321)
 + * Reduce maxHintsInProgress (CASSANDRA-8415)
 + * BTree updates may call provided update function twice (CASSANDRA-8018)
 + * Release sstable references after anticompaction (CASSANDRA-8386)
 + * Handle abort() in SSTableRewriter properly (CASSANDRA-8320)
 + * Fix high size calculations for prepared statements (CASSANDRA-8231)
 + * Centralize shared executors (CASSANDRA-8055)
 + * Fix filtering for CONTAINS (KEY) relations on frozen collection
 +   clustering columns when the query is restricted to a single
 +   partition (CASSANDRA-8203)
 + * Do more aggressive entire-sstable TTL expiry checks (CASSANDRA-8243)
 + * Add more log info if readMeter is null (CASSANDRA-8238)
 + * add check of the system wall clock time at startup (CASSANDRA-8305)
 + * Support for frozen collections (CASSANDRA-7859)
 + * Fix overflow on histogram computation (CASSANDRA-8028)
 + * Have paxos reuse the timestamp generation of normal queries (CASSANDRA-7801)
 + * Fix incremental repair not remove parent session on remote (CASSANDRA-8291)
 + * Improve JBOD disk utilization (CASSANDRA-7386)
 + * Log failed host when preparing incremental repair (CASSANDRA-8228)
 + * Force config client mode in CQLSSTableWriter (CASSANDRA-8281)
 + * Fix sstableupgrade throws exception (CASSANDRA-8688)
 + * Fix hang when repairing empty keyspace (CASSANDRA-8694)
 +Merged from 2.0:
   * Fix IllegalArgumentException in dynamic snitch (CASSANDRA-8448)
   * Add support for UPDATE ... IF EXISTS (CASSANDRA-8610)
   * Fix reversal of list prepends (CASSANDRA-8733)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/07ffe1b1/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 633d43c,2fa57b9..08777c7
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@@ -85,10 -80,12 +85,12 @@@ public class SelectStatement implement
      private final Restriction[] columnRestrictions;
  
      /** Restrictions on non-primary key columns (i.e. secondary index restrictions) */
 -    private final Map<CFDefinition.Name, Restriction> metadataRestrictions = new HashMap<CFDefinition.Name, Restriction>();
 +    private final Map<ColumnIdentifier, Restriction> metadataRestrictions = new HashMap<ColumnIdentifier, Restriction>();
  
-     // All restricted columns not covered by the key or index filter
-     private final Set<ColumnDefinition> restrictedColumns = new HashSet<ColumnDefinition>();
+     // The map keys are the name of the columns that must be converted into IndexExpressions if a secondary index need
+     // to be used. The value specify if the column has an index that can be used to for the relation in which the column
+     // is specified.
 -    private final Map<CFDefinition.Name, Boolean> restrictedNames = new HashMap<CFDefinition.Name, Boolean>();
++    private final Map<ColumnDefinition, Boolean> restrictedColumns = new HashMap<ColumnDefinition, Boolean>();
      private Restriction.Slice sliceRestriction;
  
      private boolean isReversed;
@@@ -1059,23 -1028,23 +1061,23 @@@
              return Collections.emptyList();
  
          List<IndexExpression> expressions = new ArrayList<IndexExpression>();
-         for (ColumnDefinition def : restrictedColumns)
 -        for (CFDefinition.Name name : restrictedNames.keySet())
++        for (ColumnDefinition def : restrictedColumns.keySet())
          {
              Restriction restriction;
 -            switch (name.kind)
 +            switch (def.kind)
              {
 -                case KEY_ALIAS:
 -                    restriction = keyRestrictions[name.position];
 +                case PARTITION_KEY:
 +                    restriction = keyRestrictions[def.position()];
                      break;
 -                case COLUMN_ALIAS:
 -                    restriction = columnRestrictions[name.position];
 +                case CLUSTERING_COLUMN:
 +                    restriction = columnRestrictions[def.position()];
                      break;
 -                case COLUMN_METADATA:
 +                case REGULAR:
                  case STATIC:
 -                    restriction = metadataRestrictions.get(name);
 +                    restriction = metadataRestrictions.get(def.name);
                      break;
                  default:
 -                    // We don't allow restricting a VALUE_ALIAS for now in prepare.
 +                    // We don't allow restricting a COMPACT_VALUE for now in prepare.
                      throw new AssertionError();
              }
  
@@@ -1097,39 -1067,27 +1099,49 @@@
                      }
                  }
              }
 +            else if (restriction.isContains())
 +            {
 +                SingleColumnRestriction.Contains contains = (SingleColumnRestriction.Contains)restriction;
 +                for (ByteBuffer value : contains.values(options))
 +                {
 +                    validateIndexedValue(def, value);
 +                    expressions.add(new IndexExpression(def.name.bytes, Operator.CONTAINS, value));
 +                }
 +                for (ByteBuffer key : contains.keys(options))
 +                {
 +                    validateIndexedValue(def, key);
 +                    expressions.add(new IndexExpression(def.name.bytes, Operator.CONTAINS_KEY, key));
 +                }
 +            }
              else
              {
-                 List<ByteBuffer> values = restriction.values(options);
+                 ByteBuffer value;
+                 if (restriction.isMultiColumn())
+                 {
 -                    List<ByteBuffer> values = restriction.values(variables);
 -                    value = values.get(name.position);
++                    List<ByteBuffer> values = restriction.values(options);
++                    value = values.get(def.position());
+                 }
+                 else
+                 {
 -                    List<ByteBuffer> values = restriction.values(variables);
++                    List<ByteBuffer> values = restriction.values(options);
+                     if (values.size() != 1)
+                         throw new InvalidRequestException("IN restrictions are not supported on indexed columns");
  
-                 if (values.size() != 1)
-                     throw new InvalidRequestException("IN restrictions are not supported on indexed columns");
+                     value = values.get(0);
+                 }
  
-                 ByteBuffer value = validateIndexedValue(def, values.get(0));
 -                validateIndexExpressionValue(value, name);
 -                expressions.add(new IndexExpression(name.name.key, IndexOperator.EQ, value));
++                validateIndexedValue(def, value);
 +                expressions.add(new IndexExpression(def.name.bytes, Operator.EQ, value));
              }
          }
 +
 +        if (usesSecondaryIndexing)
 +        {
 +            ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(columnFamily());
 +            SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
 +            secondaryIndexManager.validateIndexSearchersForQuery(expressions);
 +        }
 +        
          return expressions;
      }
  
@@@ -1503,7 -1506,7 +1515,7 @@@
              // All (or none) of the partition key columns have been specified;
              // hence there is no need to turn these restrictions into index expressions.
              if (!stmt.usesSecondaryIndexing)
-                 stmt.restrictedColumns.removeAll(cfm.partitionKeyColumns());
 -                stmt.restrictedNames.keySet().removeAll(cfDef.partitionKeys());
++                stmt.restrictedColumns.keySet().removeAll(cfm.partitionKeyColumns());
  
              if (stmt.selectsOnlyStaticColumns && stmt.hasClusteringColumnsRestriction())
                  throw new InvalidRequestException("Cannot restrict clustering columns when selecting only static columns");
@@@ -1514,21 -1517,16 +1526,15 @@@
              if (stmt.isKeyRange && hasQueriableClusteringColumnIndex)
                  stmt.usesSecondaryIndexing = true;
  
-             if (!stmt.usesSecondaryIndexing)
 -            // The clustering columns that can be used to perform a slice filtering on the secondary index do not
 -            // need to be converted into IndexExpressions. Therefore, if they are not indexed by an index that support
 -            // the relation in which they have been specified, we can removes them from the restrictedNames map.
 -            for (Name clusteringColumn : cfDef.clusteringColumns())
++            for (ColumnDefinition def : cfm.clusteringColumns())
              {
-                 for (ColumnDefinition def : cfm.clusteringColumns())
-                 {
-                     // Remove clustering column restrictions that can be handled by slices; the remainder will be
-                     // handled by filters (which may require a secondary index).
-                     Restriction restriction = stmt.columnRestrictions[def.position()];
-                     if (restriction != null)
-                     {
-                         if (restriction.canEvaluateWithSlices())
-                             stmt.restrictedColumns.remove(def);
-                         else
-                             stmt.usesSecondaryIndexing = true;
-                     }
-                 }
 -                Boolean indexed = stmt.restrictedNames.get(clusteringColumn);
++                // Remove clustering column restrictions that can be handled by slices; the remainder will be
++                // handled by filters (which may require a secondary index).
++                Boolean indexed = stmt.restrictedColumns.get(def);
+                 if (indexed == null)
+                     break;
 -                if (!indexed)
 -                    stmt.restrictedNames.remove(clusteringColumn);
++                if (!indexed && stmt.columnRestrictions[def.position()].canEvaluateWithSlices())
++                    stmt.restrictedColumns.remove(def);
              }
  
              // Even if usesSecondaryIndexing is false at this point, we'll still have to use one if
@@@ -1551,21 -1553,18 +1557,23 @@@
          }
  
          /** Returns a pair of (hasQueriableIndex, hasQueriableClusteringColumnIndex) */
 -        private boolean[] processRelationEntity(SelectStatement stmt, Relation relation, ColumnIdentifier entity, CFDefinition cfDef) throws InvalidRequestException
 +        private boolean[] processRelationEntity(SelectStatement stmt,
 +                                                SecondaryIndexManager indexManager,
 +                                                Relation relation,
 +                                                ColumnIdentifier entity,
 +                                                ColumnDefinition def) throws InvalidRequestException
          {
 -            CFDefinition.Name name = cfDef.get(entity);
 -            if (name == null)
 +            if (def == null)
                  handleUnrecognizedEntity(entity, relation);
  
-             stmt.restrictedColumns.add(def);
- 
 -            if (cfDef.cfm.getColumnDefinition(name.name.key).isIndexed() && relation.operator() == Relation.Type.EQ)
 +            SecondaryIndex index = indexManager.getIndexForColumn(def.name.bytes);
 +            if (index != null && index.supportsOperator(relation.operator()))
+             {
 -                stmt.restrictedNames.put(name, Boolean.TRUE);
 -                return new boolean[]{true, name.kind == CFDefinition.Name.Kind.COLUMN_ALIAS};
++                stmt.restrictedColumns.put(def, Boolean.TRUE);
 +                return new boolean[]{true, def.kind == ColumnDefinition.Kind.CLUSTERING_COLUMN};
+             }
 -            stmt.restrictedNames.put(name, Boolean.FALSE);
 +
++            stmt.restrictedColumns.put(def, Boolean.FALSE);
              return new boolean[]{false, false};
          }
  
@@@ -2134,38 -2126,7 +2142,38 @@@
              }
          }
  
 -        private SingleColumnRelation findInclusiveClusteringRelationForCompact(CFDefinition cfDef)
 +        /**
 +         * Checks if the specified statement will need to filter the data.
 +         *
 +         * @param stmt the statement to test.
 +         * @return <code>true</code> if the specified statement will need to filter the data, <code>false</code>
 +         * otherwise.
 +         */
 +        private static boolean needFiltering(SelectStatement stmt)
 +        {
 +            return stmt.restrictedColumns.size() > 1
 +                    || (stmt.restrictedColumns.isEmpty() && !stmt.columnFilterIsIdentity())
 +                    || (!stmt.restrictedColumns.isEmpty()
-                             && stmt.isRestrictedByMultipleContains(Iterables.getOnlyElement(stmt.restrictedColumns)));
++                            && stmt.isRestrictedByMultipleContains(Iterables.getOnlyElement(stmt.restrictedColumns.keySet())));
 +        }
 +
 +        private int indexOf(ColumnDefinition def, Selection selection)
 +        {
 +            return indexOf(def, selection.getColumns().iterator());
 +        }
 +
 +        private int indexOf(final ColumnDefinition def, Iterator<ColumnDefinition> defs)
 +        {
 +            return Iterators.indexOf(defs, new Predicate<ColumnDefinition>()
 +                                           {
 +                                               public boolean apply(ColumnDefinition n)
 +                                               {
 +                                                   return def.name.equals(n.name);
 +                                               }
 +                                           });
 +        }
 +
 +        private SingleColumnRelation findInclusiveClusteringRelationForCompact(CFMetaData cfm)
          {
              for (Relation r : whereClause)
              {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/07ffe1b1/test/unit/org/apache/cassandra/cql3/MultiColumnRelationTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/MultiColumnRelationTest.java
index 4c3ba2a,30a9226..25df030
--- a/test/unit/org/apache/cassandra/cql3/MultiColumnRelationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/MultiColumnRelationTest.java
@@@ -469,90 -1140,233 +469,184 @@@ public class MultiColumnRelationTest ex
      }
  
      @Test
 -    public void testPrepareInOneMarker() throws Throwable
 +    public void testMultipleClusteringReversedComponents() throws Throwable
      {
 -        for (String tableSuffix : new String[]{"", "_compact"})
 +        for (String compactOption : new String[]{"", " COMPACT STORAGE AND"})
          {
 -            execute("INSERT INTO %s.multiple_clustering" + tableSuffix + " (a, b, c, d) VALUES (0, 0, 0, 0)");
 -            execute("INSERT INTO %s.multiple_clustering" + tableSuffix + " (a, b, c, d) VALUES (0, 0, 1, 0)");
 -            execute("INSERT INTO %s.multiple_clustering" + tableSuffix + " (a, b, c, d) VALUES (0, 0, 1, 1)");
 +            createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY (a, b, c, d)) WITH" + compactOption + " CLUSTERING ORDER BY (b DESC, c ASC, d DESC)");
  
 -            UntypedResultSet results = executePrepared(prepare(
 -                            "SELECT * FROM %s.multiple_clustering" + tableSuffix + " WHERE a=0 AND (b, c, d) IN ?"),
 -                    options(list(tuple(0, 1, 0), tuple(0, 1, 1))));
 -            assertEquals(2, results.size());
 -            checkRow(0, results, 0, 0, 1, 0);
 -            checkRow(1, results, 0, 0, 1, 1);
 +            // b and d are reversed in the clustering order
 +            execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 0, 0);
 +            execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 1);
 +            execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, 1, 0);
 +
 +            execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 0, 0);
 +            execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 1, 1);
 +            execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, 1, 0);
 +
 +
 +            assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) > (?)", 0, 0),
 +                    row(0, 1, 0, 0),
 +                    row(0, 1, 1, 1),
 +                    row(0, 1, 1, 0)
 +            );
 +
 +            assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) >= (?)", 0, 0),
 +                    row(0, 1, 0, 0),
 +                    row(0, 1, 1, 1),
 +                    row(0, 1, 1, 0),
 +                    row(0, 0, 0, 0),
 +                    row(0, 0, 1, 1),
 +                    row(0, 0, 1, 0)
 +            );
 +
 +            assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) < (?)", 0, 1),
 +                    row(0, 0, 0, 0),
 +                    row(0, 0, 1, 1),
 +                    row(0, 0, 1, 0)
 +            );
 +
 +            assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) <= (?)", 0, 1),
 +                    row(0, 1, 0, 0),
 +                    row(0, 1, 1, 1),
 +                    row(0, 1, 1, 0),
 +                    row(0, 0, 0, 0),
 +                    row(0, 0, 1, 1),
 +                    row(0, 0, 1, 0)
 +            );
 +
 +            assertRows(execute("SELECT * FROM %s WHERE a=? AND (b, c, d) IN ((?, ?, ?), (?, ?, ?))", 0, 1, 1, 1, 0, 1, 1),
 +                    row(0, 1, 1, 1),
 +                    row(0, 0, 1, 1)
 +            );
  
              // same query, but reversed order for the IN values
 -            results = executePrepared(prepare(
 -                            "SELECT * FROM %s.multiple_clustering" + tableSuffix + " WHERE a=0 AND (b, c, d) IN ?"),
 -                    options(list(tuple(0, 1, 1), tuple(0, 1, 0))));
 -            assertEquals(2, results.size());
 -            checkRow(0, results, 0, 0, 1, 0);
 -            checkRow(1, results, 0, 0, 1, 1);
 -
 -            results = executePrepared(prepare(
 -                            "SELECT * FROM %s.multiple_clustering" + tableSuffix + " WHERE a=0 AND (b, c, d) IN ?"),
 -                    options(list()));
 -            assertTrue(results.isEmpty());
 -
 -            results = executePrepared(prepare("SELECT * FROM %s.multiple_clustering" + tableSuffix + " WHERE a=0 and (b, c) IN ?"),
 -                    options(list(tuple(0, 1))));
 -            assertEquals(2, results.size());
 -            checkRow(0, results, 0, 0, 1, 0);
 -            checkRow(1, results, 0, 0, 1, 1);
 -
 -            results = executePrepared(prepare("SELECT * FROM %s.multiple_clustering" + tableSuffix + " WHERE a=0 and (b) IN ?"),
 -                    options(list(tuple(0))));
 -            assertEquals(3, results.size());
 -            checkRow(0, results, 0, 0, 0, 0);
 -            checkRow(1, results, 0, 0, 1, 0);
 -            checkRow(2, results, 0, 0, 1, 1);
 -
 -            results = executePrepared(prepare("SELECT * FROM %s.multiple_clustering" + tableSuffix + " WHERE a=0 and (b) IN ?"),
 -                    options(list()));
 -            assertTrue(results.isEmpty());
 +            assertRows(execute("SELECT * FROM %s WHERE a=? AND (b, c, d) IN ((?, ?, ?), (?, ?, ?))", 0, 0, 1, 1, 1, 1, 1),
 +                    row(0, 1, 1, 1),
 +                    row(0, 0, 1, 1)
 +            );
 +
 +            assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) IN (?, ?, ?, ?, ?, ?)",
 +                            0, tuple(1, 0, 0), tuple(1, 1, 1), tuple(1, 1, 0), tuple(0, 0, 0), tuple(0, 1, 1), tuple(0, 1, 0)),
 +                    row(0, 1, 0, 0),
 +                    row(0, 1, 1, 1),
 +                    row(0, 1, 1, 0),
 +                    row(0, 0, 0, 0),
 +                    row(0, 0, 1, 1),
 +                    row(0, 0, 1, 0)
 +            );
 +
 +            assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c) IN (?)", 0, tuple(0, 1)),
 +                    row(0, 0, 1, 1),
 +                    row(0, 0, 1, 0)
 +            );
 +
 +            assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c) IN (?)", 0, tuple(0, 0)),
 +                    row(0, 0, 0, 0)
 +            );
 +
 +            assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) IN ((?))", 0, 0),
 +                    row(0, 0, 0, 0),
 +                    row(0, 0, 1, 1),
 +                    row(0, 0, 1, 0)
 +            );
 +
 +            // preserve pre-6875 behavior (even though the query result is technically incorrect)
 +            assertEmpty(execute("SELECT * FROM %s WHERE a = ? AND (b, c) > (?, ?)", 0, 1, 0));
          }
      }
+ 
+     @Test
+     public void testMultipleClusteringWithIndex() throws Throwable
+     {
 -        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 0, 0, 0, 0)");
 -        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 0, 1, 0, 1)");
 -        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 0, 1, 1, 2)");
 -        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 1, 0, 0, 0)");
 -        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 1, 1, 0, 1)");
 -        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 1, 1, 1, 2)");
 -        execute("INSERT INTO %s.multiple_clustering_with_indices (a, b, c, d, e) VALUES (0, 2, 0, 0, 0)");
 -
 -        UntypedResultSet results = execute("SELECT * FROM %s.multiple_clustering_with_indices WHERE (b) = (1)");
 -        assertEquals(3, results.size());
 -        checkRow(0, results, 0, 1, 0, 0, 0);
 -        checkRow(1, results, 0, 1, 1, 0, 1);
 -        checkRow(2, results, 0, 1, 1, 1, 2);
 -
 -        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) = (1, 1) ALLOW FILTERING");
 -        assertEquals(2, results.size());
 -        checkRow(0, results, 0, 1, 1, 0, 1);
 -        checkRow(1, results, 0, 1, 1, 1, 2);
 -
 -        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) = (1, 1) AND e = 2 ALLOW FILTERING");
 -        assertEquals(1, results.size());
 -        checkRow(0, results, 0, 1, 1, 1, 2);
 -
 -        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b) IN ((1)) AND e = 2 ALLOW FILTERING");
 -        assertEquals(1, results.size());
 -        checkRow(0, results, 0, 1, 1, 1, 2);
 -
 -        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b) IN ((0), (1)) AND e = 2 ALLOW FILTERING");
 -        assertEquals(2, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 2);
 -        checkRow(1, results, 0, 1, 1, 1, 2);
 -
 -        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) IN ((0, 1)) AND e = 2 ALLOW FILTERING");
 -        assertEquals(1, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 2);
 -
 -        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) IN ((0, 1), (1, 1)) AND e = 2 ALLOW FILTERING");
 -        assertEquals(2, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 2);
 -        checkRow(1, results, 0, 1, 1, 1, 2);
 -
 -        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b) >= (1) AND e = 2 ALLOW FILTERING");
 -        assertEquals(1, results.size());
 -        checkRow(0, results, 0, 1, 1, 1, 2);
 -
 -        results = execute("SELECT * FROM %s.multiple_clustering_with_indices  WHERE (b, c) >= (1, 1) AND e = 2 ALLOW FILTERING");
 -        assertEquals(1, results.size());
 -        checkRow(0, results, 0, 1, 1, 1, 2);
++        createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, PRIMARY KEY (a, b, c, d))");
++        createIndex("CREATE INDEX ON %s (b)");
++        createIndex("CREATE INDEX ON %s (e)");
++        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 0, 0, 0, 0);
++        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 0, 1, 0, 1);
++        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 0, 1, 1, 2);
++        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, 0);
++        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 1);
++        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 1, 2);
++        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, 0, 0, 0);
++        assertRows(execute("SELECT * FROM %s WHERE (b) = (?)", 1),
++                   row(0, 1, 0, 0, 0),
++                   row(0, 1, 1, 0, 1),
++                   row(0, 1, 1, 1, 2));
++        assertRows(execute("SELECT * FROM %s WHERE (b, c) = (?, ?) ALLOW FILTERING", 1, 1),
++                   row(0, 1, 1, 0, 1),
++                   row(0, 1, 1, 1, 2));
++        assertRows(execute("SELECT * FROM %s WHERE (b, c) = (?, ?) AND e = ? ALLOW FILTERING", 1, 1, 2),
++                   row(0, 1, 1, 1, 2));
++        assertRows(execute("SELECT * FROM %s WHERE (b) IN ((?)) AND e = ?", 1, 2),
++                   row(0, 1, 1, 1, 2));
++
++        assertRows(execute("SELECT * FROM %s WHERE (b) IN ((?), (?)) AND e = ?", 0, 1, 2),
++                   row(0, 0, 1, 1, 2),
++                   row(0, 1, 1, 1, 2));
++
++        assertRows(execute("SELECT * FROM %s WHERE (b, c) IN ((?, ?)) AND e = ?", 0, 1, 2),
++                   row(0, 0, 1, 1, 2));
++
++        assertRows(execute("SELECT * FROM %s WHERE (b, c) IN ((?, ?), (?, ?)) AND e = ?", 0, 1, 1, 1, 2),
++                   row(0, 0, 1, 1, 2),
++                   row(0, 1, 1, 1, 2));
++
++        assertRows(execute("SELECT * FROM %s WHERE (b) >= (?) AND e = ?", 1, 2),
++                   row(0, 1, 1, 1, 2));
++
++        assertRows(execute("SELECT * FROM %s WHERE (b, c) >= (?, ?) AND e = ?", 1, 1, 2),
++                   row(0, 1, 1, 1, 2));
+     }
+ 
+     @Test
 -    public void testPartitionWithIndex() throws Throwable
++    public void testMultiplePartitionKeyAndMultiClusteringWithIndex() throws Throwable
+     {
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 0, 0, 0)");
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 1, 0, 1)");
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 1, 1, 2)");
 -
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 0, 0, 3)");
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 1, 0, 4)");
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 1, 1, 5)");
 -
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 2, 0, 0, 5)");
 -
 -        UntypedResultSet results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c) = (1) ALLOW FILTERING");
 -        assertEquals(3, results.size());
 -        checkRow(0, results, 0, 0, 1, 0, 0, 3);
 -        checkRow(1, results, 0, 0, 1, 1, 0, 4);
 -        checkRow(2, results, 0, 0, 1, 1, 1, 5);
 -
 -        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c, d) = (1, 1) ALLOW FILTERING");
 -        assertEquals(2, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 0, 4);
 -        checkRow(1, results, 0, 0, 1, 1, 1, 5);
 -
 -        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0  AND (c) IN ((1)) AND f = 5 ALLOW FILTERING");
 -        assertEquals(1, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 1, 5);
 -
 -        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c) IN ((1), (2)) AND f = 5 ALLOW FILTERING");
 -        assertEquals(2, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 1, 5);
 -        checkRow(1, results, 0, 0, 2, 0, 0, 5);
 -
 -        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0  AND (c, d) IN ((1, 0)) AND f = 3 ALLOW FILTERING");
 -        assertEquals(1, results.size());
 -        checkRow(0, results, 0, 0, 1, 0, 0, 3);
 -
 -        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c) >= (1) AND f = 5 ALLOW FILTERING");
 -        assertEquals(2, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 1, 5);
 -        checkRow(1, results, 0, 0, 2, 0, 0, 5);
 -
 -        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c, d) >= (1, 1) AND f = 5 ALLOW FILTERING");
 -        assertEquals(2, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 1, 5);
 -        checkRow(1, results, 0, 0, 2, 0, 0, 5);
 -    }
++        createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, f int, PRIMARY KEY ((a, b), c, d, e))");
++        createIndex("CREATE INDEX ON %s (c)");
++        createIndex("CREATE INDEX ON %s (f)");
+ 
 -    @Test(expected=InvalidRequestException.class)
 -    public void testMissingPartitionComponentWithInRestrictionOnIndexedColumn() throws Throwable
 -    {
 -        execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c, d) IN ((1, 1)) ALLOW FILTERING");
 -    }
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 0, 0, 0, 0);
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 0, 1, 0, 1);
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 0, 1, 1, 2);
+ 
 -    @Test(expected=InvalidRequestException.class)
 -    public void testMissingPartitionComponentWithSliceRestrictionOnIndexedColumn() throws Throwable
 -    {
 -        execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND (c, d) >= (1, 1) ALLOW FILTERING");
 -    }
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 1, 0, 0, 3);
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 1, 1, 0, 4);
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 1, 1, 1, 5);
+ 
 -    @Test(expected=InvalidRequestException.class)
 -    public void testPrepareLiteralInWithShortTuple() throws Throwable
 -    {
 -        prepare("SELECT * FROM %s.multiple_clustering WHERE a=0 AND (b, c, d) IN ((?, ?))");
 -    }
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 2, 0, 0, 5);
+ 
 -    @Test(expected=InvalidRequestException.class)
 -    public void testPrepareLiteralInWithLongTuple() throws Throwable
 -    {
 -        prepare("SELECT * FROM %s.multiple_clustering WHERE a=0 AND (b, c, d) IN ((?, ?, ?, ?, ?))");
 -    }
++        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));
+ 
 -    @Test(expected=InvalidRequestException.class)
 -    public void testPrepareLiteralInWithPartitionKey() throws Throwable
 -    {
 -        prepare("SELECT * FROM %s.multiple_clustering WHERE (a, b, c, d) IN ((?, ?, ?, ?))");
 -    }
++        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));
+ 
 -    @Test(expected=InvalidRequestException.class)
 -    public void testPrepareLiteralInSkipsClusteringColumn() throws Throwable
 -    {
 -        prepare("SELECT * FROM %s.multiple_clustering WHERE (c, d) IN ((?, ?))");
 -    }
++        assertInvalidMessage("Partition key part b must be restricted since preceding part is",
++                             "SELECT * FROM %s WHERE a = ? AND (c, d) IN ((?, ?)) ALLOW FILTERING", 0, 1, 1);
+ 
 -    private static QueryOptions makeIntOptions(Integer... values)
 -    {
 -        List<ByteBuffer> buffers = new ArrayList<>(values.length);
 -        for (int value : values)
 -            buffers.add(ByteBufferUtil.bytes(value));
 -        return new QueryOptions(ConsistencyLevel.ONE, buffers);
 -    }
++        assertInvalidMessage("Partition key part b must be restricted since preceding part is",
++                             "SELECT * FROM %s WHERE a = ? AND (c, d) >= (?, ?) ALLOW FILTERING", 0, 1, 1);
+ 
 -    private static ByteBuffer tuple(Integer... values)
 -    {
 -        List<AbstractType<?>> types = new ArrayList<>(values.length);
 -        ByteBuffer[] buffers = new ByteBuffer[values.length];
 -        for (int i = 0; i < values.length; i++)
 -        {
 -            types.add(Int32Type.instance);
 -            buffers[i] = ByteBufferUtil.bytes(values[i]);
 -        }
++        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c) IN ((?)) AND f = ? ALLOW FILTERING", 0, 1, 5),
++                   row(0, 0, 1, 1, 1, 5));
+ 
 -        TupleType type = new TupleType(types);
 -        return type.buildValue(buffers);
 -    }
++        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c) IN ((?), (?)) AND f = ? ALLOW FILTERING", 0, 1, 2, 5),
++                   row(0, 0, 1, 1, 1, 5),
++                   row(0, 0, 2, 0, 0, 5));
+ 
 -    private static ByteBuffer list(ByteBuffer... values)
 -    {
 -        return CollectionType.pack(Arrays.asList(values), values.length);
 -    }
++        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c, d) IN ((?, ?)) AND f = ? ALLOW FILTERING", 0, 1, 0, 3),
++                   row(0, 0, 1, 0, 0, 3));
+ 
 -    private static QueryOptions options(ByteBuffer... buffers)
 -    {
 -        return new QueryOptions(ConsistencyLevel.ONE, Arrays.asList(buffers));
 -    }
++        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c) >= (?) AND f = ? ALLOW FILTERING", 0, 1, 5),
++                   row(0, 0, 1, 1, 1, 5),
++                   row(0, 0, 2, 0, 0, 5));
+ 
 -    private static void checkRow(int rowIndex, UntypedResultSet results, Integer... expectedValues)
 -    {
 -        List<UntypedResultSet.Row> rows = newArrayList(results.iterator());
 -        UntypedResultSet.Row row = rows.get(rowIndex);
 -        Iterator<ColumnSpecification> columns = row.getColumns().iterator();
 -        for (Integer expected : expectedValues)
 -        {
 -            String columnName = columns.next().name.toString();
 -            int actual = row.getInt(columnName);
 -            assertEquals(String.format("Expected value %d for column %s in row %d, but got %s", actual, columnName, rowIndex, expected),
 -                         (long) expected, actual);
 -        }
++        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c, d) >= (?, ?) AND f = ? ALLOW FILTERING", 0, 1, 1, 5),
++                   row(0, 0, 1, 1, 1, 5),
++                   row(0, 0, 2, 0, 0, 5));
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/07ffe1b1/test/unit/org/apache/cassandra/cql3/SingleColumnRelationTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/SingleColumnRelationTest.java
index 2ad4bda,34d3bf1..604ec60
--- a/test/unit/org/apache/cassandra/cql3/SingleColumnRelationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/SingleColumnRelationTest.java
@@@ -17,51 -17,129 +17,91 @@@
   */
  package org.apache.cassandra.cql3;
  
 -import java.util.Iterator;
 -import java.util.List;
 -
 -import org.junit.AfterClass;
 -import org.junit.BeforeClass;
  import org.junit.Test;
  
 -import org.apache.cassandra.SchemaLoader;
 -import org.apache.cassandra.db.ConsistencyLevel;
 -import org.apache.cassandra.exceptions.InvalidRequestException;
 -import org.apache.cassandra.gms.Gossiper;
 -import org.apache.cassandra.service.ClientState;
 -
 -import static com.google.common.collect.Lists.newArrayList;
 -import static org.apache.cassandra.cql3.QueryProcessor.process;
 -import static org.apache.cassandra.cql3.QueryProcessor.processInternal;
 -import static org.junit.Assert.assertEquals;
 +import java.util.ArrayList;
 +import java.util.List;
  
 -public class SingleColumnRelationTest
 +public class SingleColumnRelationTest extends CQLTester
  {
 -    static ClientState clientState;
 -    static String keyspace = "single_column_relation_test";
 -
 -    @BeforeClass
 -    public static void setUpClass() throws Throwable
 -    {
 -        SchemaLoader.loadSchema();
 -        executeSchemaChange("CREATE KEYSPACE IF NOT EXISTS %s WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}");
 -
 -        executeSchemaChange("CREATE TABLE IF NOT EXISTS %s.partition_with_indices (a int, b int, c int, d int, e int, f int, PRIMARY KEY ((a, b), c, d, e))");
 -        executeSchemaChange("CREATE INDEX ON %s.partition_with_indices (c)");
 -        executeSchemaChange("CREATE INDEX ON %s.partition_with_indices (f)");
 -
 -        clientState = ClientState.forInternalCalls();
 -    }
 -
 -    @AfterClass
 -    public static void stopGossiper()
 +    @Test
 +    public void testInvalidCollectionEqualityRelation() throws Throwable
      {
 -        Gossiper.instance.stop();
 +        createTable("CREATE TABLE %s (a int PRIMARY KEY, b set<int>, c list<int>, d map<int, int>)");
 +        createIndex("CREATE INDEX ON %s (b)");
 +        createIndex("CREATE INDEX ON %s (c)");
 +        createIndex("CREATE INDEX ON %s (d)");
 +
 +        assertInvalid("SELECT * FROM %s WHERE a = 0 AND b=?", set(0));
 +        assertInvalid("SELECT * FROM %s WHERE a = 0 AND c=?", list(0));
 +        assertInvalid("SELECT * FROM %s WHERE a = 0 AND d=?", map(0, 0));
      }
  
 -    private static void executeSchemaChange(String query) throws Throwable
 +    @Test
 +    public void testInvalidCollectionNonEQRelation() throws Throwable
      {
 -        try
 -        {
 -            process(String.format(query, keyspace), ConsistencyLevel.ONE);
 -        } catch (RuntimeException exc)
 -        {
 -            throw exc.getCause();
 -        }
 +        createTable("CREATE TABLE %s (a int PRIMARY KEY, b set<int>, c int)");
 +        createIndex("CREATE INDEX ON %s (c)");
 +        execute("INSERT INTO %s (a, b, c) VALUES (0, {0}, 0)");
 +
 +        // non-EQ operators
 +        assertInvalid("SELECT * FROM %s WHERE c = 0 AND b > ?", set(0));
 +        assertInvalid("SELECT * FROM %s WHERE c = 0 AND b >= ?", set(0));
 +        assertInvalid("SELECT * FROM %s WHERE c = 0 AND b < ?", set(0));
 +        assertInvalid("SELECT * FROM %s WHERE c = 0 AND b <= ?", set(0));
 +        assertInvalid("SELECT * FROM %s WHERE c = 0 AND b IN (?)", set(0));
      }
  
 -    private static UntypedResultSet execute(String query) throws Throwable
 +    @Test
 +    public void testLargeClusteringINValues() throws Throwable
      {
 -        try
 -        {
 -            return processInternal(String.format(query, keyspace));
 -        } catch (RuntimeException exc)
 -        {
 -            if (exc.getCause() != null)
 -                throw exc.getCause();
 -            throw exc;
 -        }
 +        createTable("CREATE TABLE %s (k int, c int, v int, PRIMARY KEY (k, c))");
 +        execute("INSERT INTO %s (k, c, v) VALUES (0, 0, 0)");
 +        List<Integer> inValues = new ArrayList<>(10000);
 +        for (int i = 0; i < 10000; i++)
 +            inValues.add(i);
 +        assertRows(execute("SELECT * FROM %s WHERE k=? AND c IN ?", 0, inValues),
 +                row(0, 0, 0)
 +        );
      }
+ 
+     @Test
 -    public void testPartitionWithIndex() throws Throwable
++    public void testMultiplePartitionKeyWithIndex() throws Throwable
+     {
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 0, 0, 0)");
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 1, 0, 1)");
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 0, 1, 1, 2)");
 -
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 0, 0, 3)");
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 1, 0, 4)");
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 1, 1, 1, 5)");
 -
 -        execute("INSERT INTO %s.partition_with_indices (a, b, c, d, e, f) VALUES (0, 0, 2, 0, 0, 5)");
 -
 -        UntypedResultSet results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c = 1 ALLOW FILTERING");
 -        assertEquals(3, results.size());
 -        checkRow(0, results, 0, 0, 1, 0, 0, 3);
 -        checkRow(1, results, 0, 0, 1, 1, 0, 4);
 -        checkRow(2, results, 0, 0, 1, 1, 1, 5);
 -
 -        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c = 1 AND d = 1 ALLOW FILTERING");
 -        assertEquals(2, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 0, 4);
 -        checkRow(1, results, 0, 0, 1, 1, 1, 5);
 -
 -        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c >= 1 AND f = 5 ALLOW FILTERING");
 -        assertEquals(2, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 1, 5);
 -        checkRow(1, results, 0, 0, 2, 0, 0, 5);
 -
 -        results = execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c = 1 AND d >= 1 AND f = 5 ALLOW FILTERING");
 -        assertEquals(1, results.size());
 -        checkRow(0, results, 0, 0, 1, 1, 1, 5);
 -    }
++        createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, f int, PRIMARY KEY ((a, b), c, d, e))");
++        createIndex("CREATE INDEX ON %s (c)");
++        createIndex("CREATE INDEX ON %s (f)");
+ 
 -    @Test(expected=InvalidRequestException.class)
 -    public void testMissingPartitionComponentAndFileringOnTheSecondClusteringColumnWithoutAllowFiltering() throws Throwable
 -    {
 -        execute("SELECT * FROM %s.partition_with_indices WHERE d >= 1 AND f = 5");
 -    }
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 0, 0, 0, 0);
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 0, 1, 0, 1);
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 0, 1, 1, 2);
+ 
 -    @Test(expected=InvalidRequestException.class)
 -    public void testMissingPartitionComponentWithSliceRestrictionOnIndexedColumn() throws Throwable
 -    {
 -        execute("SELECT * FROM %s.partition_with_indices WHERE a = 0 AND c >= 1 ALLOW FILTERING");
 -    }
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 1, 0, 0, 3);
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 1, 1, 0, 4);
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 1, 1, 1, 5);
+ 
 -    private static void checkRow(int rowIndex, UntypedResultSet results, Integer... expectedValues)
 -    {
 -        List<UntypedResultSet.Row> rows = newArrayList(results.iterator());
 -        UntypedResultSet.Row row = rows.get(rowIndex);
 -        Iterator<ColumnSpecification> columns = row.getColumns().iterator();
 -        for (Integer expected : expectedValues)
 -        {
 -            String columnName = columns.next().name.toString();
 -            int actual = row.getInt(columnName);
 -            assertEquals(String.format("Expected value %d for column %s in row %d, but got %s", actual, columnName, rowIndex, expected),
 -                         (long) expected, actual);
 -        }
++        execute("INSERT INTO %s (a, b, c, d, e, f) VALUES (?, ?, ?, ?, ?, ?)", 0, 0, 2, 0, 0, 5);
++
++        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));
++
++        assertRows(execute("SELECT * FROM %s WHERE a = ? AND c = ? AND d = ? ALLOW FILTERING", 0, 1, 1),
++                   row(0, 0, 1, 1, 0, 4),
++                   row(0, 0, 1, 1, 1, 5));
++
++        assertInvalidMessage("Partition key part b must be restricted since preceding part is",
++                             "SELECT * FROM %s WHERE a = ? AND c >= ? ALLOW FILTERING", 0, 1);
++
++        assertRows(execute("SELECT * FROM %s WHERE a = ? AND c >= ? AND f = ? ALLOW FILTERING", 0, 1, 5),
++                   row(0, 0, 1, 1, 1, 5),
++                   row(0, 0, 2, 0, 0, 5));
++
++        assertRows(execute("SELECT * FROM %s WHERE a = ? AND c = ? AND d >= ? AND f = ? ALLOW FILTERING", 0, 1, 1, 5),
++                   row(0, 0, 1, 1, 1, 5));
++
++        assertInvalidMessage("Cannot execute this query as it might involve data filtering and thus may have unpredictable performance. If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING",
++                             "SELECT * FROM %s WHERE a = ? AND d >= ? AND f = ?", 0, 1, 5);
+     }
  }