Refactor Restriction hierarchy

patch by Benjamin Lerer; reviewed by Tyler Hobbs for CASSANDRA-11354


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

Branch: refs/heads/trunk
Commit: 831bebdba86ac1956852bd216a4cc62d898c87d7
Parents: dc569c9
Author: Benjamin Lerer <[email protected]>
Authored: Fri Apr 15 21:04:25 2016 +0200
Committer: Benjamin Lerer <[email protected]>
Committed: Fri Apr 15 21:13:52 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |    1 +
 .../AbstractPrimaryKeyRestrictions.java         |   61 -
 .../cql3/restrictions/AbstractRestriction.java  |  108 -
 .../ClusteringColumnRestrictions.java           |  179 ++
 .../ForwardingPrimaryKeyRestrictions.java       |  197 --
 .../restrictions/MultiColumnRestriction.java    |   34 +-
 .../restrictions/PartitionKeyRestrictions.java  |   51 +
 .../PartitionKeySingleRestrictionSet.java       |  132 ++
 .../restrictions/PrimaryKeyRestrictionSet.java  |  339 ----
 .../restrictions/PrimaryKeyRestrictions.java    |   46 -
 .../cql3/restrictions/Restriction.java          |   70 +-
 .../cql3/restrictions/RestrictionSet.java       |   85 +-
 .../restrictions/RestrictionSetWrapper.java     |   95 +
 .../cql3/restrictions/Restrictions.java         |   54 +-
 .../restrictions/SingleColumnRestriction.java   |   43 +-
 .../cql3/restrictions/SingleRestriction.java    |  117 ++
 .../restrictions/StatementRestrictions.java     |   62 +-
 .../cql3/restrictions/TokenFilter.java          |   85 +-
 .../cql3/restrictions/TokenRestriction.java     |   85 +-
 .../apache/cassandra/cql3/statements/Bound.java |   13 +
 .../ClusteringColumnRestrictionsTest.java       | 1919 ++++++++++++++++++
 .../PrimaryKeyRestrictionSetTest.java           | 1919 ------------------
 22 files changed, 2748 insertions(+), 2947 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index c91a4cd..acbbfa5 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.6
+ * Refactor Restriction hierarchy (CASSANDRA-11354)
  * Eliminate allocations in R/W path (CASSANDRA-11421)
  * Update Netty to 4.0.36 (CASSANDRA-11567)
  * Fix PER PARTITION LIMIT for queries requiring post-query ordering 
(CASSANDRA-11556)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
 
b/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
deleted file mode 100644
index f1b5a50..0000000
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/AbstractPrimaryKeyRestrictions.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.nio.ByteBuffer;
-import java.util.*;
-
-import org.apache.cassandra.cql3.QueryOptions;
-import org.apache.cassandra.cql3.statements.Bound;
-import org.apache.cassandra.db.ClusteringPrefix;
-import org.apache.cassandra.db.ClusteringComparator;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-
-/**
- * Base class for <code>PrimaryKeyRestrictions</code>.
- */
-abstract class AbstractPrimaryKeyRestrictions extends AbstractRestriction 
implements PrimaryKeyRestrictions
-{
-    /**
-     * The composite type.
-     */
-    protected final ClusteringComparator comparator;
-
-    public AbstractPrimaryKeyRestrictions(ClusteringComparator comparator)
-    {
-        this.comparator = comparator;
-    }
-
-    @Override
-    public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws 
InvalidRequestException
-    {
-        return values(options);
-    }
-
-    @Override
-    public final boolean isEmpty()
-    {
-        return getColumnDefs().isEmpty();
-    }
-
-    @Override
-    public final int size()
-    {
-        return getColumnDefs().size();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java 
b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
deleted file mode 100644
index 4d6240c..0000000
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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 org.apache.cassandra.cql3.QueryOptions;
-import org.apache.cassandra.cql3.statements.Bound;
-import org.apache.cassandra.db.MultiCBuilder;
-
-import org.apache.cassandra.config.ColumnDefinition;
-
-/**
- * Base class for <code>Restriction</code>s
- */
-abstract class AbstractRestriction  implements Restriction
-{
-    @Override
-    public  boolean isOnToken()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean isMultiColumn()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean isSlice()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean isEQ()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean isLIKE()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean isIN()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean isContains()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean isNotNull()
-    {
-        return false;
-    }
-
-    @Override
-    public boolean hasBound(Bound b)
-    {
-        return true;
-    }
-
-    @Override
-    public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, 
QueryOptions options)
-    {
-        return appendTo(builder, options);
-    }
-
-    @Override
-    public boolean isInclusive(Bound b)
-    {
-        return true;
-    }
-
-    /**
-     * Reverses the specified bound if the column type is a reversed one.
-     *
-     * @param columnDefinition the column definition
-     * @param bound the bound
-     * @return the bound reversed if the column type was a reversed one or the 
original bound
-     */
-    protected static Bound reverseBoundIfNeeded(ColumnDefinition 
columnDefinition, Bound bound)
-    {
-        return columnDefinition.isReversedType() ? bound.reverse() : bound;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java
 
b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java
new file mode 100644
index 0000000..47cf76b
--- /dev/null
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java
@@ -0,0 +1,179 @@
+/*
+ * 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;
+
+    public ClusteringColumnRestrictions(CFMetaData cfm)
+    {
+        super(new RestrictionSet());
+        this.comparator = cfm.comparator;
+    }
+
+    private ClusteringColumnRestrictions(ClusteringComparator comparator, 
RestrictionSet restrictionSet)
+    {
+        super(restrictionSet);
+        this.comparator = comparator;
+    }
+
+    public ClusteringColumnRestrictions mergeWith(Restriction restriction) 
throws InvalidRequestException
+    {
+        SingleRestriction newRestriction = (SingleRestriction) restriction;
+        RestrictionSet newRestrictionSet = 
restrictions.addRestriction(newRestriction);
+
+        if (!isEmpty())
+        {
+            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);
+    }
+
+    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<Slice.Bound> boundsAsClustering(Bound bound, 
QueryOptions options) throws InvalidRequestException
+    {
+        MultiCBuilder builder = MultiCBuilder.create(comparator, hasIN() || 
hasMultiColumnSlice());
+        int keyPosition = 0;
+        for (SingleRestriction r : restrictions)
+        {
+            ColumnDefinition def = r.getFirstColumn();
+
+            if (keyPosition != def.position() || r.isContains() || r.isLIKE())
+                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);
+    }
+
+    @Override
+    public void addRowFilterTo(RowFilter filter,
+                               SecondaryIndexManager indexManager,
+                               QueryOptions options) throws 
InvalidRequestException
+    {
+        int position = 0;
+
+        for (SingleRestriction restriction : restrictions)
+        {
+            ColumnDefinition columnDef = restriction.getFirstColumn();
+
+            // We ignore all the clustering columns that can be handled by 
slices.
+            if (!(restriction.isContains() || restriction.isLIKE()) && 
position == columnDef.position())
+            {
+                position = restriction.getLastColumn().position() + 1;
+                if (!restriction.hasSupportingIndex(indexManager))
+                    continue;
+            }
+            restriction.addRowFilterTo(filter, indexManager, options);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
 
b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
deleted file mode 100644
index c9cd1d2..0000000
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * 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.nio.ByteBuffer;
-import java.util.List;
-import java.util.NavigableSet;
-
-import org.apache.cassandra.config.ColumnDefinition;
-import org.apache.cassandra.cql3.QueryOptions;
-import org.apache.cassandra.cql3.functions.Function;
-import org.apache.cassandra.cql3.statements.Bound;
-import org.apache.cassandra.db.Clustering;
-import org.apache.cassandra.db.MultiCBuilder;
-import org.apache.cassandra.db.Slice;
-import org.apache.cassandra.db.filter.RowFilter;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-import org.apache.cassandra.index.SecondaryIndexManager;
-
-/**
- * A <code>PrimaryKeyRestrictions</code> which forwards all its method calls 
to another 
- * <code>PrimaryKeyRestrictions</code>. Subclasses should override one or more 
methods to modify the behavior 
- * of the backing <code>PrimaryKeyRestrictions</code> as desired per the 
decorator pattern. 
- */
-abstract class ForwardingPrimaryKeyRestrictions implements 
PrimaryKeyRestrictions
-{
-    /**
-     * Returns the backing delegate instance that methods are forwarded to.
-     * @return the backing delegate instance that methods are forwarded to.
-     */
-    protected abstract PrimaryKeyRestrictions getDelegate();
-
-    @Override
-    public Iterable<Function> getFunctions()
-    {
-        return getDelegate().getFunctions();
-    }
-
-    @Override
-    public List<ColumnDefinition> getColumnDefs()
-    {
-        return getDelegate().getColumnDefs();
-    }
-
-    @Override
-    public ColumnDefinition getFirstColumn()
-    {
-        return getDelegate().getFirstColumn();
-    }
-
-    @Override
-    public ColumnDefinition getLastColumn()
-    {
-        return getDelegate().getLastColumn();
-    }
-
-    @Override
-    public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws 
InvalidRequestException
-    {
-        return getDelegate().mergeWith(restriction);
-    }
-
-    @Override
-    public boolean hasSupportingIndex(SecondaryIndexManager 
secondaryIndexManager)
-    {
-        return getDelegate().hasSupportingIndex(secondaryIndexManager);
-    }
-
-    @Override
-    public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException
-    {
-        return getDelegate().values(options);
-    }
-
-    @Override
-    public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions options)
-    {
-        return getDelegate().appendTo(builder, options);
-    }
-
-    @Override
-    public NavigableSet<Clustering> valuesAsClustering(QueryOptions options) 
throws InvalidRequestException
-    {
-        return getDelegate().valuesAsClustering(options);
-    }
-
-    @Override
-    public List<ByteBuffer> bounds(Bound bound, QueryOptions options) throws 
InvalidRequestException
-    {
-        return getDelegate().bounds(bound, options);
-    }
-
-    @Override
-    public NavigableSet<Slice.Bound> boundsAsClustering(Bound bound, 
QueryOptions options) throws InvalidRequestException
-    {
-        return getDelegate().boundsAsClustering(bound, options);
-    }
-
-    @Override
-    public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, 
QueryOptions options)
-    {
-        return getDelegate().appendBoundTo(builder, bound, options);
-    }
-
-    @Override
-    public boolean isInclusive(Bound bound)
-    {
-        return getDelegate().isInclusive(bound.reverse());
-    }
-
-    @Override
-    public boolean isEmpty()
-    {
-        return getDelegate().isEmpty();
-    }
-
-    @Override
-    public int size()
-    {
-        return getDelegate().size();
-    }
-
-    @Override
-    public boolean isOnToken()
-    {
-        return getDelegate().isOnToken();
-    }
-
-    @Override
-    public boolean isSlice()
-    {
-        return getDelegate().isSlice();
-    }
-
-    @Override
-    public boolean isEQ()
-    {
-        return getDelegate().isEQ();
-    }
-
-    @Override
-    public boolean isLIKE()
-    {
-        return getDelegate().isLIKE();
-    }
-
-    @Override
-    public boolean isIN()
-    {
-        return getDelegate().isIN();
-    }
-
-    @Override
-    public boolean isContains()
-    {
-        return getDelegate().isContains();
-    }
-
-    @Override
-    public boolean isNotNull()
-    {
-        return getDelegate().isNotNull();
-    }
-
-    @Override
-    public boolean isMultiColumn()
-    {
-        return getDelegate().isMultiColumn();
-    }
-
-    @Override
-    public boolean hasBound(Bound b)
-    {
-        return getDelegate().hasBound(b);
-    }
-
-    @Override
-    public void addRowFilterTo(RowFilter filter, SecondaryIndexManager 
indexManager, QueryOptions options) throws InvalidRequestException
-    {
-        getDelegate().addRowFilterTo(filter, indexManager, options);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java 
b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
index ec02d06..f2f530d 100644
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
@@ -27,7 +27,6 @@ import org.apache.cassandra.cql3.functions.Function;
 import org.apache.cassandra.cql3.statements.Bound;
 import org.apache.cassandra.db.MultiCBuilder;
 import org.apache.cassandra.db.filter.RowFilter;
-import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.index.Index;
 import org.apache.cassandra.index.SecondaryIndexManager;
 
@@ -36,7 +35,7 @@ import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkNotNu
 import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
 import static 
org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
 
-public abstract class MultiColumnRestriction extends AbstractRestriction
+public abstract class MultiColumnRestriction implements SingleRestriction
 {
     /**
      * The columns to which the restriction apply.
@@ -73,7 +72,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
     }
 
     @Override
-    public final Restriction mergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+    public final SingleRestriction mergeWith(SingleRestriction 
otherRestriction)
     {
         // We want to allow query like: (b,c) > (?, ?) AND b < ?
         if (!otherRestriction.isMultiColumn()
@@ -85,7 +84,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         return doMergeWith(otherRestriction);
     }
 
-    protected abstract Restriction doMergeWith(Restriction otherRestriction) 
throws InvalidRequestException;
+    protected abstract SingleRestriction doMergeWith(SingleRestriction 
otherRestriction);
 
     /**
      * Returns the names of the columns that are specified within this 
<code>Restrictions</code> and the other one
@@ -150,7 +149,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+        public SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             throw invalidRequest("%s cannot be restricted by more than one 
relation if it includes an Equal",
                                  getColumnsInCommons(otherRestriction));
@@ -179,7 +178,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public final void addRowFilterTo(RowFilter filter, 
SecondaryIndexManager indexMananger, QueryOptions options) throws 
InvalidRequestException
+        public final void addRowFilterTo(RowFilter filter, 
SecondaryIndexManager indexMananger, QueryOptions options)
         {
             Tuples.Value t = ((Tuples.Value) value.bind(options));
             List<ByteBuffer> values = t.getElements();
@@ -220,7 +219,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+        public SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             throw invalidRequest("%s cannot be restricted by more than one 
relation if it includes a IN",
                                  getColumnsInCommons(otherRestriction));
@@ -238,7 +237,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         @Override
         public final void addRowFilterTo(RowFilter filter,
                                          SecondaryIndexManager indexManager,
-                                         QueryOptions options) throws 
InvalidRequestException
+                                         QueryOptions options)
         {
             List<List<ByteBuffer>> splitInValues = splitValues(options);
             checkTrue(splitInValues.size() == 1, "IN restrictions are not 
supported on indexed columns");
@@ -251,7 +250,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
             }
         }
 
-        protected abstract List<List<ByteBuffer>> splitValues(QueryOptions 
options) throws InvalidRequestException;
+        protected abstract List<List<ByteBuffer>> splitValues(QueryOptions 
options);
     }
 
     /**
@@ -281,7 +280,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        protected List<List<ByteBuffer>> splitValues(QueryOptions options) 
throws InvalidRequestException
+        protected List<List<ByteBuffer>> splitValues(QueryOptions options)
         {
             List<List<ByteBuffer>> buffers = new ArrayList<>(values.size());
             for (Term value : values)
@@ -320,7 +319,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        protected List<List<ByteBuffer>> splitValues(QueryOptions options) 
throws InvalidRequestException
+        protected List<List<ByteBuffer>> splitValues(QueryOptions options)
         {
             Tuples.InMarker inMarker = (Tuples.InMarker) marker;
             Tuples.InValue inValue = inMarker.bind(options);
@@ -371,7 +370,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
             for (int i = 0, m = columnDefs.size(); i < m; i++)
             {
                 ColumnDefinition column = columnDefs.get(i);
-                Bound b = reverseBoundIfNeeded(column, bound);
+                Bound b = bound.reverseIfNeeded(column);
 
                 // For mixed order columns, we need to create additional 
slices when 2 columns are in reverse order
                 if (reversed != column.isReversedType())
@@ -445,7 +444,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+        public SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             checkTrue(otherRestriction.isSlice(),
                       "Column \"%s\" cannot be restricted by both an equality 
and an inequality relation",
@@ -476,7 +475,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         @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");
         }
@@ -493,9 +492,8 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
          * @param b the bound type
          * @param options the query options
          * @return one ByteBuffer per-component in the bound
-         * @throws InvalidRequestException if the components cannot be 
retrieved
          */
-        private List<ByteBuffer> componentBounds(Bound b, QueryOptions 
options) throws InvalidRequestException
+        private List<ByteBuffer> componentBounds(Bound b, QueryOptions options)
         {
             if (!slice.hasBound(b))
                 return Collections.emptyList();
@@ -543,7 +541,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+        public SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             throw invalidRequest("%s cannot be restricted by a relation if it 
includes an IS NOT NULL clause",
                                  getColumnsInCommons(otherRestriction));
@@ -565,7 +563,7 @@ public abstract class MultiColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public final void addRowFilterTo(RowFilter filter, 
SecondaryIndexManager indexMananger, QueryOptions options) throws 
InvalidRequestException
+        public final void addRowFilterTo(RowFilter filter, 
SecondaryIndexManager indexMananger, QueryOptions options)
         {
             throw new UnsupportedOperationException("Secondary indexes do not 
support IS NOT NULL restrictions");
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java 
b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java
new file mode 100644
index 0000000..10efa9f
--- /dev/null
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeyRestrictions.java
@@ -0,0 +1,51 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+
+/**
+ * A set of restrictions on the partition key.
+ *
+ */
+interface PartitionKeyRestrictions extends Restrictions
+{
+    public PartitionKeyRestrictions mergeWith(Restriction restriction);
+
+    public List<ByteBuffer> values(QueryOptions options);
+
+    public List<ByteBuffer> bounds(Bound b, QueryOptions options);
+
+    /**
+     * Checks if the specified bound is set or not.
+     * @param b the bound type
+     * @return <code>true</code> if the specified bound is set, 
<code>false</code> otherwise
+     */
+    public boolean hasBound(Bound b);
+
+    /**
+     * Checks if the specified bound is inclusive or not.
+     * @param b the bound type
+     * @return <code>true</code> if the specified bound is inclusive, 
<code>false</code> otherwise
+     */
+    public boolean isInclusive(Bound b);
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java
 
b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java
new file mode 100644
index 0000000..b96f6da
--- /dev/null
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java
@@ -0,0 +1,132 @@
+/*
+ * 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.nio.ByteBuffer;
+import java.util.*;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.ClusteringComparator;
+import org.apache.cassandra.db.ClusteringPrefix;
+import org.apache.cassandra.db.MultiCBuilder;
+import org.apache.cassandra.db.filter.RowFilter;
+import org.apache.cassandra.index.SecondaryIndexManager;
+
+/**
+ * A set of single restrictions on the partition key.
+ * <p>This class can only contains <code>SingleRestriction</code> instances. 
Token restrictions will be handled by
+ * <code>TokenRestriction</code> class or by the <code>TokenFilter</code> 
class if the query contains a mix of token
+ * restrictions and single column restrictions on the partition key.
+ */
+final class PartitionKeySingleRestrictionSet extends RestrictionSetWrapper 
implements PartitionKeyRestrictions
+{
+    /**
+     * The composite type.
+     */
+    protected final ClusteringComparator comparator;
+
+    public PartitionKeySingleRestrictionSet(ClusteringComparator comparator)
+    {
+        super(new RestrictionSet());
+        this.comparator = comparator;
+    }
+
+    private PartitionKeySingleRestrictionSet(PartitionKeySingleRestrictionSet 
restrictionSet,
+                                       SingleRestriction restriction)
+    {
+        super(restrictionSet.restrictions.addRestriction(restriction));
+        this.comparator = restrictionSet.comparator;
+    }
+
+    private List<ByteBuffer> toByteBuffers(SortedSet<? extends 
ClusteringPrefix> clusterings)
+    {
+        List<ByteBuffer> l = new ArrayList<>(clusterings.size());
+        for (ClusteringPrefix clustering : clusterings)
+            l.add(CFMetaData.serializePartitionKey(clustering));
+        return l;
+    }
+
+    @Override
+    public PartitionKeyRestrictions mergeWith(Restriction restriction)
+    {
+        if (restriction.isOnToken())
+        {
+            if (isEmpty())
+                return (PartitionKeyRestrictions) restriction;
+
+            return new TokenFilter(this, (TokenRestriction) restriction);
+        }
+
+        return new PartitionKeySingleRestrictionSet(this, (SingleRestriction) 
restriction);
+    }
+
+    @Override
+    public List<ByteBuffer> values(QueryOptions options)
+    {
+        MultiCBuilder builder = MultiCBuilder.create(comparator, hasIN());
+        for (SingleRestriction r : restrictions)
+        {
+            r.appendTo(builder, options);
+            if (builder.hasMissingElements())
+                break;
+        }
+        return toByteBuffers(builder.build());
+    }
+
+    @Override
+    public List<ByteBuffer> bounds(Bound bound, QueryOptions options)
+    {
+        MultiCBuilder builder = MultiCBuilder.create(comparator, hasIN());
+        for (SingleRestriction r : restrictions)
+        {
+            r.appendBoundTo(builder, bound, options);
+            if (builder.hasMissingElements())
+                return Collections.emptyList();
+        }
+        return toByteBuffers(builder.buildBound(bound.isStart(), true));
+    }
+
+    @Override
+    public boolean hasBound(Bound b)
+    {
+        if (isEmpty())
+            return false;
+        return restrictions.lastRestriction().hasBound(b);
+    }
+
+    @Override
+    public boolean isInclusive(Bound b)
+    {
+        if (isEmpty())
+            return false;
+        return restrictions.lastRestriction().isInclusive(b);
+    }
+
+    @Override
+    public void addRowFilterTo(RowFilter filter,
+                               SecondaryIndexManager indexManager,
+                               QueryOptions options)
+    {
+        for (SingleRestriction restriction : restrictions)
+        {
+             restriction.addRowFilterTo(filter, indexManager, options);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java 
b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
deleted file mode 100644
index 7d0c3df..0000000
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * 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.nio.ByteBuffer;
-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.functions.Function;
-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 single column restrictions on a primary key part (partition key or 
clustering key).
- */
-final class PrimaryKeyRestrictionSet extends AbstractPrimaryKeyRestrictions
-{
-    /**
-     * The restrictions.
-     */
-    private final RestrictionSet restrictions;
-
-    /**
-     * <code>true</code> if the restrictions are corresponding to an EQ, 
<code>false</code> otherwise.
-     */
-    private boolean eq;
-
-    /**
-     * <code>true</code> if the restrictions are corresponding to an IN, 
<code>false</code> otherwise.
-     */
-    private boolean in;
-
-    /**
-     * <code>true</code> if the restrictions are corresponding to a LIKE, 
<code>false</code> otherwise.
-     */
-    private boolean like;
-
-    /**
-     * <code>true</code> if the restrictions are corresponding to a Slice, 
<code>false</code> otherwise.
-     */
-    private boolean slice;
-
-    /**
-     * <code>true</code> if the restrictions are corresponding to a Contains, 
<code>false</code> otherwise.
-     */
-    private boolean contains;
-
-    /**
-     * <code>true</code> if the restrictions corresponding to a partition key, 
<code>false</code> if it's clustering columns.
-     */
-    private boolean isPartitionKey;
-
-    public PrimaryKeyRestrictionSet(ClusteringComparator comparator, boolean 
isPartitionKey)
-    {
-        super(comparator);
-        this.restrictions = new RestrictionSet();
-        this.eq = true;
-        this.isPartitionKey = isPartitionKey;
-    }
-
-    private PrimaryKeyRestrictionSet(PrimaryKeyRestrictionSet 
primaryKeyRestrictions,
-                                               Restriction restriction) throws 
InvalidRequestException
-    {
-        super(primaryKeyRestrictions.comparator);
-        this.restrictions = 
primaryKeyRestrictions.restrictions.addRestriction(restriction);
-        this.isPartitionKey = primaryKeyRestrictions.isPartitionKey;
-
-        if (!primaryKeyRestrictions.isEmpty())
-        {
-            ColumnDefinition lastRestrictionStart = 
primaryKeyRestrictions.restrictions.lastRestriction().getFirstColumn();
-            ColumnDefinition newRestrictionStart = 
restriction.getFirstColumn();
-
-            checkFalse(primaryKeyRestrictions.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() && restriction.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);
-        }
-
-        if (restriction.isSlice() || primaryKeyRestrictions.isSlice())
-            this.slice = true;
-        else if (restriction.isContains() || 
primaryKeyRestrictions.isContains())
-            this.contains = true;
-        else if (restriction.isIN() || primaryKeyRestrictions.isIN())
-            this.in = true;
-        else if (restriction.isLIKE() || primaryKeyRestrictions.isLIKE())
-            this.like = true;
-        else
-            this.eq = true;
-    }
-
-    private List<ByteBuffer> toByteBuffers(SortedSet<? extends 
ClusteringPrefix> clusterings)
-    {
-        // It's currently a tad hard to follow that this is only called for 
partition key so we should fix that
-        List<ByteBuffer> l = new ArrayList<>(clusterings.size());
-        for (ClusteringPrefix clustering : clusterings)
-            l.add(CFMetaData.serializePartitionKey(clustering));
-        return l;
-    }
-
-    @Override
-    public boolean isSlice()
-    {
-        return slice;
-    }
-
-    @Override
-    public boolean isEQ()
-    {
-        return eq;
-    }
-
-    @Override
-    public boolean isIN()
-    {
-        return in;
-    }
-
-    @Override
-    public boolean isLIKE()
-    {
-        return like;
-    }
-
-    @Override
-    public boolean isContains()
-    {
-        return contains;
-    }
-
-    @Override
-    public Iterable<Function> getFunctions()
-    {
-        return restrictions.getFunctions();
-    }
-
-    @Override
-    public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws 
InvalidRequestException
-    {
-        if (restriction.isOnToken())
-        {
-            if (isEmpty())
-                return (PrimaryKeyRestrictions) restriction;
-
-            return new TokenFilter(this, (TokenRestriction) restriction);
-        }
-
-        return new PrimaryKeyRestrictionSet(this, restriction);
-    }
-
-    // Whether any of the underlying restriction is an IN
-    private boolean hasIN()
-    {
-        if (isIN())
-            return true;
-
-        for (Restriction restriction : restrictions)
-        {
-            if (restriction.isIN())
-                return true;
-        }
-        return false;
-    }
-
-    private boolean hasMultiColumnSlice()
-    {
-        for (Restriction restriction : restrictions)
-        {
-            if (restriction.isMultiColumn() && restriction.isSlice())
-                return true;
-        }
-        return false;
-    }
-
-    @Override
-    public NavigableSet<Clustering> valuesAsClustering(QueryOptions options) 
throws InvalidRequestException
-    {
-        return appendTo(MultiCBuilder.create(comparator, hasIN()), 
options).build();
-    }
-
-    @Override
-    public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions options)
-    {
-        for (Restriction r : restrictions)
-        {
-            r.appendTo(builder, options);
-            if (builder.hasMissingElements())
-                break;
-        }
-        return builder;
-    }
-
-    @Override
-    public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, 
QueryOptions options)
-    {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public NavigableSet<Slice.Bound> boundsAsClustering(Bound bound, 
QueryOptions options) throws InvalidRequestException
-    {
-        MultiCBuilder builder = MultiCBuilder.create(comparator, hasIN() || 
hasMultiColumnSlice());
-        int keyPosition = 0;
-        for (Restriction r : restrictions)
-        {
-            ColumnDefinition def = r.getFirstColumn();
-
-            if (keyPosition != def.position() || r.isContains() || r.isLIKE())
-                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);
-    }
-
-    @Override
-    public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException
-    {
-        if (!isPartitionKey)
-            throw new UnsupportedOperationException();
-
-        return toByteBuffers(valuesAsClustering(options));
-    }
-
-    @Override
-    public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws 
InvalidRequestException
-    {
-        if (!isPartitionKey)
-            throw new UnsupportedOperationException();
-
-        return toByteBuffers(boundsAsClustering(b, options));
-    }
-
-    @Override
-    public boolean hasBound(Bound b)
-    {
-        if (isEmpty())
-            return false;
-        return restrictions.lastRestriction().hasBound(b);
-    }
-
-    @Override
-    public boolean isInclusive(Bound b)
-    {
-        if (isEmpty())
-            return false;
-        return restrictions.lastRestriction().isInclusive(b);
-    }
-
-    @Override
-    public boolean hasSupportingIndex(SecondaryIndexManager indexManager)
-    {
-        return restrictions.hasSupportingIndex(indexManager);
-    }
-
-    @Override
-    public void addRowFilterTo(RowFilter filter,
-                               SecondaryIndexManager indexManager,
-                               QueryOptions options) throws 
InvalidRequestException
-    {
-        int position = 0;
-
-        for (Restriction restriction : restrictions)
-        {
-            ColumnDefinition columnDef = restriction.getFirstColumn();
-
-            // We ignore all the clustering columns that can be handled by 
slices.
-            if (!isPartitionKey && !(restriction.isContains() || 
restriction.isLIKE()) && position == columnDef.position())
-            {
-                position = restriction.getLastColumn().position() + 1;
-                if (!restriction.hasSupportingIndex(indexManager))
-                    continue;
-            }
-            restriction.addRowFilterTo(filter, indexManager, options);
-        }
-    }
-
-    @Override
-    public List<ColumnDefinition> getColumnDefs()
-    {
-        return restrictions.getColumnDefs();
-    }
-
-    @Override
-    public ColumnDefinition getFirstColumn()
-    {
-        return restrictions.firstColumn();
-    }
-
-    @Override
-    public ColumnDefinition getLastColumn()
-    {
-        return restrictions.lastColumn();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java 
b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
deleted file mode 100644
index 2f9cd7b..0000000
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictions.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.nio.ByteBuffer;
-import java.util.List;
-import java.util.NavigableSet;
-
-import org.apache.cassandra.cql3.QueryOptions;
-import org.apache.cassandra.cql3.statements.Bound;
-import org.apache.cassandra.db.Clustering;
-import org.apache.cassandra.db.Slice;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-
-/**
- * A set of restrictions on a primary key part (partition key or clustering 
key).
- *
- */
-interface PrimaryKeyRestrictions extends Restriction, Restrictions
-{
-    @Override
-    public PrimaryKeyRestrictions mergeWith(Restriction restriction) throws 
InvalidRequestException;
-
-    public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException;
-
-    public NavigableSet<Clustering> valuesAsClustering(QueryOptions options) 
throws InvalidRequestException;
-
-    public List<ByteBuffer> bounds(Bound b, QueryOptions options) throws 
InvalidRequestException;
-
-    public NavigableSet<Slice.Bound> boundsAsClustering(Bound bound, 
QueryOptions options) throws InvalidRequestException;
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java 
b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
index d3c525e..8311220 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
@@ -22,28 +22,18 @@ import java.util.List;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.cql3.QueryOptions;
 import org.apache.cassandra.cql3.functions.Function;
-import org.apache.cassandra.cql3.statements.Bound;
-import org.apache.cassandra.db.MultiCBuilder;
 import org.apache.cassandra.db.filter.RowFilter;
-import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.index.SecondaryIndexManager;
 
 /**
- * A restriction/clause on a column.
- * The goal of this class being to group all conditions for a column in a 
SELECT.
- *
- * <p>Implementation of this class must be immutable. See {@link 
#mergeWith(Restriction)} for more explanation.</p>
+ * <p>Implementation of this class must be immutable.</p>
  */
 public interface Restriction
 {
-    public boolean isOnToken();
-    public boolean isSlice();
-    public boolean isEQ();
-    public boolean isLIKE();
-    public boolean isIN();
-    public boolean isContains();
-    public boolean isNotNull();
-    public boolean isMultiColumn();
+    public default boolean isOnToken()
+    {
+        return false;
+    }
 
     /**
      * Returns the definition of the first column.
@@ -71,33 +61,6 @@ public interface Restriction
     public Iterable<Function> getFunctions();
 
     /**
-     * Checks if the specified bound is set or not.
-     * @param b the bound type
-     * @return <code>true</code> if the specified bound is set, 
<code>false</code> otherwise
-     */
-    public boolean hasBound(Bound b);
-
-    /**
-     * Checks if the specified bound is inclusive or not.
-     * @param b the bound type
-     * @return <code>true</code> if the specified bound is inclusive, 
<code>false</code> otherwise
-     */
-    public boolean isInclusive(Bound b);
-
-    /**
-     * Merges this restriction with the specified one.
-     *
-     * <p>Restriction are immutable. Therefore merging two restrictions result 
in a new one.
-     * The reason behind this choice is that it allow a great flexibility in 
the way the merging can done while
-     * preventing any side effect.</p>
-     *
-     * @param otherRestriction the restriction to merge into this one
-     * @return the restriction resulting of the merge
-     * @throws InvalidRequestException if the restrictions cannot be merged
-     */
-    public Restriction mergeWith(Restriction otherRestriction) throws 
InvalidRequestException;
-
-    /**
      * Check if the restriction is on indexed columns.
      *
      * @param indexManager the index manager
@@ -111,29 +74,8 @@ public interface Restriction
      * @param filter the row filter to add expressions to
      * @param indexManager the secondary index manager
      * @param options the query options
-     * @throws InvalidRequestException if this <code>Restriction</code> cannot 
be converted into a row filter
      */
     public void addRowFilterTo(RowFilter filter,
                                SecondaryIndexManager indexManager,
-                               QueryOptions options)
-                               throws InvalidRequestException;
-
-    /**
-     * Appends the values of this <code>Restriction</code> to the specified 
builder.
-     *
-     * @param builder the <code>MultiCBuilder</code> to append to.
-     * @param options the query options
-     * @return the <code>MultiCBuilder</code>
-     */
-    public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions options);
-
-    /**
-     * Appends the values of the <code>Restriction</code> for the specified 
bound to the specified builder.
-     *
-     * @param builder the <code>MultiCBuilder</code> to append to.
-     * @param bound the bound
-     * @param options the query options
-     * @return the <code>MultiCBuilder</code>
-     */
-    public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, 
QueryOptions options);
+                               QueryOptions options);
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java 
b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
index c9aec0e..8011e92 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
@@ -18,6 +18,7 @@
 package org.apache.cassandra.cql3.restrictions;
 
 import java.util.*;
+import java.util.stream.Stream;
 
 import com.google.common.collect.Iterables;
 
@@ -32,10 +33,9 @@ import org.apache.cassandra.index.SecondaryIndexManager;
 /**
  * Sets of column restrictions.
  *
- * <p>This class is immutable in order to be use within {@link 
PrimaryKeyRestrictionSet} which as
- * an implementation of {@link Restriction} need to be immutable.
+ * <p>This class is immutable.</p>
  */
-final class RestrictionSet implements Restrictions, Iterable<Restriction>
+final class RestrictionSet implements Restrictions, Iterable<SingleRestriction>
 {
     /**
      * The comparator used to sort the <code>Restriction</code>s.
@@ -53,14 +53,14 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
     /**
      * The restrictions per column.
      */
-    protected final TreeMap<ColumnDefinition, Restriction> restrictions;
+    protected final TreeMap<ColumnDefinition, SingleRestriction> restrictions;
 
     public RestrictionSet()
     {
-        this(new TreeMap<ColumnDefinition, 
Restriction>(COLUMN_DEFINITION_COMPARATOR));
+        this(new TreeMap<ColumnDefinition, 
SingleRestriction>(COLUMN_DEFINITION_COMPARATOR));
     }
 
-    private RestrictionSet(TreeMap<ColumnDefinition, Restriction> restrictions)
+    private RestrictionSet(TreeMap<ColumnDefinition, SingleRestriction> 
restrictions)
     {
         this.restrictions = restrictions;
     }
@@ -78,6 +78,11 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
         return new ArrayList<>(restrictions.keySet());
     }
 
+    public Stream<SingleRestriction> stream()
+    {
+        return new LinkedHashSet<>(restrictions.values()).stream();
+    }
+
     @Override
     public Iterable<Function> getFunctions()
     {
@@ -110,21 +115,19 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
      *
      * @param restriction the restriction to add
      * @return the new set of restrictions
-     * @throws InvalidRequestException if the new restriction cannot be added
      */
-    public RestrictionSet addRestriction(Restriction restriction) throws 
InvalidRequestException
+    public RestrictionSet addRestriction(SingleRestriction restriction)
     {
         // RestrictionSet is immutable so we need to clone the restrictions 
map.
-        TreeMap<ColumnDefinition, Restriction> newRestrictions = new 
TreeMap<>(this.restrictions);
+        TreeMap<ColumnDefinition, SingleRestriction> newRestrictions = new 
TreeMap<>(this.restrictions);
         return new RestrictionSet(mergeRestrictions(newRestrictions, 
restriction));
     }
 
-    private TreeMap<ColumnDefinition, Restriction> 
mergeRestrictions(TreeMap<ColumnDefinition, Restriction> restrictions,
-                                                                     
Restriction restriction)
-                                                                     throws 
InvalidRequestException
+    private TreeMap<ColumnDefinition, SingleRestriction> 
mergeRestrictions(TreeMap<ColumnDefinition, SingleRestriction> restrictions,
+                                                                           
SingleRestriction restriction)
     {
         Collection<ColumnDefinition> columnDefs = restriction.getColumnDefs();
-        Set<Restriction> existingRestrictions = getRestrictions(columnDefs);
+        Set<SingleRestriction> existingRestrictions = 
getRestrictions(columnDefs);
 
         if (existingRestrictions.isEmpty())
         {
@@ -133,9 +136,9 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
         }
         else
         {
-            for (Restriction existing : existingRestrictions)
+            for (SingleRestriction existing : existingRestrictions)
             {
-                Restriction newRestriction = mergeRestrictions(existing, 
restriction);
+                SingleRestriction newRestriction = mergeRestrictions(existing, 
restriction);
 
                 for (ColumnDefinition columnDef : columnDefs)
                     restrictions.put(columnDef, newRestriction);
@@ -151,12 +154,12 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
      * @param columnDefs the column definitions
      * @return all the restrictions applied to the specified columns
      */
-    private Set<Restriction> getRestrictions(Collection<ColumnDefinition> 
columnDefs)
+    private Set<SingleRestriction> 
getRestrictions(Collection<ColumnDefinition> columnDefs)
     {
-        Set<Restriction> set = new HashSet<>();
+        Set<SingleRestriction> set = new HashSet<>();
         for (ColumnDefinition columnDef : columnDefs)
         {
-            Restriction existing = restrictions.get(columnDef);
+            SingleRestriction existing = restrictions.get(columnDef);
             if (existing != null)
                 set.add(existing);
         }
@@ -185,22 +188,14 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
         return restrictions.tailMap(columnDef, false).firstKey();
     }
 
-    /**
-     * Returns the definition of the first column.
-     *
-     * @return the definition of the first column.
-     */
-    ColumnDefinition firstColumn()
+    @Override
+    public ColumnDefinition getFirstColumn()
     {
         return isEmpty() ? null : this.restrictions.firstKey();
     }
 
-    /**
-     * Returns the definition of the last column.
-     *
-     * @return the definition of the last column.
-     */
-    ColumnDefinition lastColumn()
+    @Override
+    public ColumnDefinition getLastColumn()
     {
         return isEmpty() ? null : this.restrictions.lastKey();
     }
@@ -210,7 +205,7 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
      *
      * @return the last restriction.
      */
-    Restriction lastRestriction()
+    SingleRestriction lastRestriction()
     {
         return isEmpty() ? null : this.restrictions.lastEntry().getValue();
     }
@@ -223,8 +218,8 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
      * @return the merged restriction
      * @throws InvalidRequestException if the two restrictions cannot be merged
      */
-    private static Restriction mergeRestrictions(Restriction restriction,
-                                                 Restriction otherRestriction) 
throws InvalidRequestException
+    private static SingleRestriction mergeRestrictions(SingleRestriction 
restriction,
+                                                       SingleRestriction 
otherRestriction)
     {
         return restriction == null ? otherRestriction
                                    : restriction.mergeWith(otherRestriction);
@@ -239,7 +234,7 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
     public final boolean hasMultipleContains()
     {
         int numberOfContains = 0;
-        for (Restriction restriction : restrictions.values())
+        for (SingleRestriction restriction : restrictions.values())
         {
             if (restriction.isContains())
             {
@@ -251,8 +246,28 @@ final class RestrictionSet implements Restrictions, 
Iterable<Restriction>
     }
 
     @Override
-    public Iterator<Restriction> iterator()
+    public Iterator<SingleRestriction> iterator()
     {
         return new LinkedHashSet<>(restrictions.values()).iterator();
     }
+
+    /**
+     * Checks if any of the underlying restriction is an IN.
+     * @return <code>true</code> if any of the underlying restriction is an 
IN, <code>false</code> otherwise
+     */
+    public final boolean hasIN()
+    {
+        return stream().anyMatch(SingleRestriction::isIN);
+    }
+
+    /**
+     * Checks if all of the underlying restrictions are EQ or IN restrictions.
+     *
+     * @return <code>true</code> if all of the underlying restrictions are EQ 
or IN restrictions,
+     * <code>false</code> otherwise
+     */
+    public final boolean hasOnlyEqualityRestrictions()
+    {
+        return stream().allMatch(p -> p.isEQ() || p.isIN());
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java 
b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java
new file mode 100644
index 0000000..b8ccb1c
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java
@@ -0,0 +1,95 @@
+/*
+ * 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.List;
+
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.db.filter.RowFilter;
+import org.apache.cassandra.index.SecondaryIndexManager;
+
+/**
+ * A <code>RestrictionSet</code> wrapper that can be extended to allow to 
modify the <code>RestrictionSet</code>
+ * behaviour without breaking its immutability. Sub-classes should be 
immutables.
+ */
+class RestrictionSetWrapper implements Restrictions
+{
+    /**
+     * The wrapped <code>RestrictionSet</code>.
+     */
+    protected final RestrictionSet restrictions;
+
+    public RestrictionSetWrapper(RestrictionSet restrictions)
+    {
+        this.restrictions = restrictions;
+    }
+
+    public void addRowFilterTo(RowFilter filter,
+                               SecondaryIndexManager indexManager,
+                               QueryOptions options)
+    {
+        restrictions.addRowFilterTo(filter, indexManager, options);
+    }
+
+    public List<ColumnDefinition> getColumnDefs()
+    {
+        return restrictions.getColumnDefs();
+    }
+
+    public Iterable<Function> getFunctions()
+    {
+        return restrictions.getFunctions();
+    }
+
+    public boolean isEmpty()
+    {
+        return restrictions.isEmpty();
+    }
+
+    public int size()
+    {
+        return restrictions.size();
+    }
+
+    public boolean hasSupportingIndex(SecondaryIndexManager indexManager)
+    {
+        return restrictions.hasSupportingIndex(indexManager);
+    }
+
+    public ColumnDefinition getFirstColumn()
+    {
+        return restrictions.getFirstColumn();
+    }
+
+    public ColumnDefinition getLastColumn()
+    {
+        return restrictions.getLastColumn();
+    }
+
+    public boolean hasIN()
+    {
+        return restrictions.hasIN();
+    }
+
+    public boolean hasOnlyEqualityRestrictions()
+    {
+        return restrictions.hasOnlyEqualityRestrictions();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java 
b/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java
index f46f176..7ca82ab 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/Restrictions.java
@@ -17,62 +17,36 @@
  */
 package org.apache.cassandra.cql3.restrictions;
 
-import java.util.Collection;
-
-import org.apache.cassandra.config.ColumnDefinition;
-import org.apache.cassandra.cql3.QueryOptions;
-import org.apache.cassandra.cql3.functions.Function;
-import org.apache.cassandra.db.filter.RowFilter;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-import org.apache.cassandra.index.SecondaryIndexManager;
-
 /**
  * Sets of restrictions
  */
-public interface Restrictions
+public interface Restrictions extends Restriction
 {
     /**
-     * Returns the column definitions in position order.
-     * @return the column definitions in position order.
-     */
-    public Collection<ColumnDefinition> getColumnDefs();
-
-    /**
-     * Return an Iterable over all of the functions (both native and 
user-defined) used by any component
-     * of the restrictions
-     * @return functions all functions found (may contain duplicates)
-     */
-    public Iterable<Function> getFunctions();
-
-    /**
-     * Check if the restriction is on indexed columns.
+     * Checks if this <code>PrimaryKeyRestrictionSet</code> is empty or not.
      *
-     * @param indexManager the index manager
-     * @return <code>true</code> if the restriction is on indexed columns, 
<code>false</code>
+     * @return <code>true</code> if this <code>PrimaryKeyRestrictionSet</code> 
is empty, <code>false</code> otherwise.
      */
-    public boolean hasSupportingIndex(SecondaryIndexManager indexManager);
+    boolean isEmpty();
 
     /**
-     * Adds to the specified row filter the expressions corresponding to this 
<code>Restrictions</code>.
+     * Returns the number of columns that have a restriction.
      *
-     * @param filter the row filter to add expressions to
-     * @param indexManager the secondary index manager
-     * @param options the query options
-     * @throws InvalidRequestException if this <code>Restrictions</code> 
cannot be converted into a row filter
+     * @return the number of columns that have a restriction.
      */
-    public void addRowFilterTo(RowFilter filter, SecondaryIndexManager 
indexManager, QueryOptions options) throws InvalidRequestException;
+    public int size();
 
     /**
-     * Checks if this <code>PrimaryKeyRestrictionSet</code> is empty or not.
-     *
-     * @return <code>true</code> if this <code>PrimaryKeyRestrictionSet</code> 
is empty, <code>false</code> otherwise.
+     * Checks if any of the underlying restriction is an IN.
+     * @return <code>true</code> if any of the underlying restriction is an 
IN, <code>false</code> otherwise
      */
-    boolean isEmpty();
+    public boolean hasIN();
 
     /**
-     * Returns the number of columns that have a restriction.
+     * Checks if all of the underlying restrictions are EQ or IN restrictions.
      *
-     * @return the number of columns that have a restriction.
+     * @return <code>true</code> if all of the underlying restrictions are EQ 
or IN restrictions,
+     * <code>false</code> otherwise
      */
-    public int size();
+    public boolean hasOnlyEqualityRestrictions();
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java 
b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
index a5804be..9c20bef 100644
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
@@ -31,7 +31,6 @@ import org.apache.cassandra.cql3.functions.Function;
 import org.apache.cassandra.cql3.statements.Bound;
 import org.apache.cassandra.db.MultiCBuilder;
 import org.apache.cassandra.db.filter.RowFilter;
-import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.index.Index;
 import org.apache.cassandra.index.SecondaryIndexManager;
 import org.apache.cassandra.utils.ByteBufferUtil;
@@ -43,7 +42,7 @@ import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkNotNu
 import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
 import static 
org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
 
-public abstract class SingleColumnRestriction extends AbstractRestriction
+public abstract class SingleColumnRestriction implements SingleRestriction
 {
     /**
      * The definition of the column to which apply the restriction.
@@ -84,7 +83,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
     }
 
     @Override
-    public final Restriction mergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+    public final SingleRestriction mergeWith(SingleRestriction 
otherRestriction)
     {
         // We want to allow query like: b > ? AND (b,c) < (?, ?)
         if (otherRestriction.isMultiColumn() && 
canBeConvertedToMultiColumnRestriction())
@@ -95,7 +94,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         return doMergeWith(otherRestriction);
     }
 
-    protected abstract Restriction doMergeWith(Restriction otherRestriction) 
throws InvalidRequestException;
+    protected abstract SingleRestriction doMergeWith(SingleRestriction 
otherRestriction);
 
     /**
      * Converts this <code>SingleColumnRestriction</code> into a {@link 
MultiColumnRestriction}
@@ -176,7 +175,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+        public SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             throw invalidRequest("%s cannot be restricted by more than one 
relation if it includes an Equal", columnDef.name);
         }
@@ -202,7 +201,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public final Restriction doMergeWith(Restriction otherRestriction) 
throws InvalidRequestException
+        public final SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             throw invalidRequest("%s cannot be restricted by more than one 
relation if it includes a IN", columnDef.name);
         }
@@ -219,7 +218,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         @Override
         public void addRowFilterTo(RowFilter filter,
                                    SecondaryIndexManager indexManager,
-                                   QueryOptions options) throws 
InvalidRequestException
+                                   QueryOptions options)
         {
             List<ByteBuffer> values = getValues(options);
             checkTrue(values.size() == 1, "IN restrictions are not supported 
on indexed columns");
@@ -233,7 +232,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
             return index.supportsExpression(columnDef, Operator.IN);
         }
 
-        protected abstract List<ByteBuffer> getValues(QueryOptions options) 
throws InvalidRequestException;
+        protected abstract List<ByteBuffer> getValues(QueryOptions options);
     }
 
     public static class InRestrictionWithValues extends INRestriction
@@ -259,7 +258,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        protected List<ByteBuffer> getValues(QueryOptions options) throws 
InvalidRequestException
+        protected List<ByteBuffer> getValues(QueryOptions options)
         {
             List<ByteBuffer> buffers = new ArrayList<>(values.size());
             for (Term value : values)
@@ -297,7 +296,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        protected List<ByteBuffer> getValues(QueryOptions options) throws 
InvalidRequestException
+        protected List<ByteBuffer> getValues(QueryOptions options)
         {
             Terminal term = marker.bind(options);
             checkNotNull(term, "Invalid null value for column %s", 
columnDef.name);
@@ -356,7 +355,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         @Override
         public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, 
QueryOptions options)
         {
-            Bound b = reverseBoundIfNeeded(getFirstColumn(), bound);
+            Bound b = bound.reverseIfNeeded(getFirstColumn());
 
             if (!hasBound(b))
                 return builder;
@@ -374,7 +373,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+        public SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             checkTrue(otherRestriction.isSlice(),
                       "Column \"%s\" cannot be restricted by both an equality 
and an inequality relation",
@@ -392,7 +391,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public void addRowFilterTo(RowFilter filter, SecondaryIndexManager 
indexManager, QueryOptions options) throws InvalidRequestException
+        public void addRowFilterTo(RowFilter filter, SecondaryIndexManager 
indexManager, QueryOptions options)
         {
             for (Bound b : Bound.values())
                 if (hasBound(b))
@@ -467,7 +466,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+        public SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             checkTrue(otherRestriction.isContains(),
                       "Collection column %s can only be restricted by 
CONTAINS, CONTAINS KEY, or map-entry equality",
@@ -482,7 +481,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public void addRowFilterTo(RowFilter filter, SecondaryIndexManager 
indexManager, QueryOptions options) throws InvalidRequestException
+        public void addRowFilterTo(RowFilter filter, SecondaryIndexManager 
indexManager, QueryOptions options)
         {
             for (ByteBuffer value : bindAndGet(values, options))
                 filter.add(columnDef, Operator.CONTAINS, value);
@@ -567,9 +566,8 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
          * @param terms the terms
          * @param options the query options
          * @return the value resulting from binding the query options to the 
specified terms
-         * @throws InvalidRequestException if a problem occurs while binding 
the query options
          */
-        private static List<ByteBuffer> bindAndGet(List<Term> terms, 
QueryOptions options) throws InvalidRequestException
+        private static List<ByteBuffer> bindAndGet(List<Term> terms, 
QueryOptions options)
         {
             List<ByteBuffer> buffers = new ArrayList<>(terms.size());
             for (Term value : terms)
@@ -643,7 +641,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+        public SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             throw invalidRequest("%s cannot be restricted by a relation if it 
includes an IS NOT NULL", columnDef.name);
         }
@@ -708,9 +706,8 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
             // there must be a suitable INDEX for LIKE_XXX expressions
             RowFilter.SimpleExpression expression = filter.add(columnDef, 
operation.left, operation.right);
             indexManager.getBestIndexFor(expression)
-                        .orElseThrow(() -> new 
InvalidRequestException(expression.toString() +
-                                                                       " is 
only supported on properly" +
-                                                                       " 
indexed columns"));
+                        .orElseThrow(() -> invalidRequest("%s is only 
supported on properly indexed columns",
+                                                          expression));
         }
 
         @Override
@@ -729,7 +726,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
         }
 
         @Override
-        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
+        public SingleRestriction doMergeWith(SingleRestriction 
otherRestriction)
         {
             throw invalidRequest("%s cannot be restricted by more than one 
relation if it includes a %s", columnDef.name, operator);
         }
@@ -778,7 +775,7 @@ public abstract class SingleColumnRestriction extends 
AbstractRestriction
             }
 
             if (endIndex == 0 || beginIndex == endIndex)
-                throw new InvalidRequestException("LIKE value can't be 
empty.");
+                throw invalidRequest("LIKE value can't be empty.");
 
             ByteBuffer newValue = value.duplicate();
             newValue.position(beginIndex);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/831bebdb/src/java/org/apache/cassandra/cql3/restrictions/SingleRestriction.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/SingleRestriction.java 
b/src/java/org/apache/cassandra/cql3/restrictions/SingleRestriction.java
new file mode 100644
index 0000000..42b0b4e
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleRestriction.java
@@ -0,0 +1,117 @@
+/*
+ * 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 org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.cql3.statements.Bound;
+import org.apache.cassandra.db.MultiCBuilder;
+
+/**
+ * A single restriction/clause on one or multiple column.
+ */
+public interface SingleRestriction extends Restriction
+{
+    public default boolean isSlice()
+    {
+        return false;
+    }
+
+    public default boolean isEQ()
+    {
+        return false;
+    }
+
+    public default boolean isLIKE()
+    {
+        return false;
+    }
+
+    public default boolean isIN()
+    {
+        return false;
+    }
+
+    public default boolean isContains()
+    {
+        return false;
+    }
+
+    public default boolean isNotNull()
+    {
+        return false;
+    }
+
+    public default boolean isMultiColumn()
+    {
+        return false;
+    }
+
+    /**
+     * Checks if the specified bound is set or not.
+     * @param b the bound type
+     * @return <code>true</code> if the specified bound is set, 
<code>false</code> otherwise
+     */
+    public default boolean hasBound(Bound b)
+    {
+        return true;
+    }
+
+    /**
+     * Checks if the specified bound is inclusive or not.
+     * @param b the bound type
+     * @return <code>true</code> if the specified bound is inclusive, 
<code>false</code> otherwise
+     */
+    public default boolean isInclusive(Bound b)
+    {
+        return true;
+    }
+
+    /**
+     * Merges this restriction with the specified one.
+     *
+     * <p>Restriction are immutable. Therefore merging two restrictions result 
in a new one.
+     * The reason behind this choice is that it allow a great flexibility in 
the way the merging can done while
+     * preventing any side effect.</p>
+     *
+     * @param otherRestriction the restriction to merge into this one
+     * @return the restriction resulting of the merge
+     */
+    public SingleRestriction mergeWith(SingleRestriction otherRestriction);
+
+    /**
+     * Appends the values of this <code>SingleRestriction</code> to the 
specified builder.
+     *
+     * @param builder the <code>MultiCBuilder</code> to append to.
+     * @param options the query options
+     * @return the <code>MultiCBuilder</code>
+     */
+    public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions options);
+
+    /**
+     * Appends the values of the <code>SingleRestriction</code> for the 
specified bound to the specified builder.
+     *
+     * @param builder the <code>MultiCBuilder</code> to append to.
+     * @param bound the bound
+     * @param options the query options
+     * @return the <code>MultiCBuilder</code>
+     */
+    public default MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound 
bound, QueryOptions options)
+    {
+        return appendTo(builder, options);
+    }
+}

Reply via email to