Merge branch 'cassandra-2.0' into cassandra-2.1

Conflicts:
        CHANGES.txt
        src/java/org/apache/cassandra/cql3/Cql.g
        src/java/org/apache/cassandra/cql3/Lists.java
        src/java/org/apache/cassandra/cql3/QueryProcessor.java
        src/java/org/apache/cassandra/cql3/Relation.java
        src/java/org/apache/cassandra/cql3/ResultSet.java
        src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
        src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
        src/java/org/apache/cassandra/cql3/statements/Restriction.java
        src/java/org/apache/cassandra/cql3/statements/SelectStatement.java


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

Branch: refs/heads/cassandra-2.1
Commit: bf5219000be9c03daa1dc4fb420b031f6ffec01d
Parents: 9bd3887 4349638
Author: Tyler Hobbs <ty...@datastax.com>
Authored: Thu May 22 14:19:42 2014 -0500
Committer: Tyler Hobbs <ty...@datastax.com>
Committed: Thu May 22 14:19:42 2014 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |    1 +
 .../apache/cassandra/cql3/AbstractMarker.java   |   10 +-
 .../org/apache/cassandra/cql3/Constants.java    |    6 +
 src/java/org/apache/cassandra/cql3/Cql.g        |  109 +-
 src/java/org/apache/cassandra/cql3/Lists.java   |   13 +-
 .../cassandra/cql3/MultiColumnRelation.java     |  144 +++
 .../org/apache/cassandra/cql3/Relation.java     |  104 +-
 .../cassandra/cql3/SingleColumnRelation.java    |   95 ++
 src/java/org/apache/cassandra/cql3/Term.java    |   10 +
 src/java/org/apache/cassandra/cql3/Tuples.java  |  349 ++++++
 .../cql3/statements/ModificationStatement.java  |   20 +-
 .../cql3/statements/MultiColumnRestriction.java |  137 +++
 .../cassandra/cql3/statements/Restriction.java  |  395 +------
 .../cql3/statements/SelectStatement.java        |  805 ++++++++-----
 .../statements/SingleColumnRestriction.java     |  413 +++++++
 .../cassandra/db/composites/CBuilder.java       |    2 +
 .../cassandra/db/composites/Composites.java     |    2 +
 .../cassandra/db/composites/CompoundCType.java  |   13 +
 .../cassandra/db/composites/SimpleCType.java    |   10 +
 .../apache/cassandra/db/marshal/TupleType.java  |  279 +++++
 .../cassandra/cql3/MultiColumnRelationTest.java | 1114 ++++++++++++++++++
 21 files changed, 3263 insertions(+), 768 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 55fc400,c6c51c3..7ab411b
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -64,7 -35,24 +64,8 @@@ Merged from 2.0
   * Fix 2ndary index queries with DESC clustering order (CASSANDRA-6950)
   * Invalid key cache entries on DROP (CASSANDRA-6525)
   * Fix flapping RecoveryManagerTest (CASSANDRA-7084)
 + * Add missing iso8601 patterns for date strings (6973)
++ * Support selecting multiple rows in a partition using IN (CASSANDRA-6875)
  Merged from 1.2:
   * Add Cloudstack snitch (CASSANDRA-7147)
   * Update system.peers correctly when relocating tokens (CASSANDRA-7126)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/AbstractMarker.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/AbstractMarker.java
index 2b9c6c9,4329ed9..0b59ed4
--- a/src/java/org/apache/cassandra/cql3/AbstractMarker.java
+++ b/src/java/org/apache/cassandra/cql3/AbstractMarker.java
@@@ -99,10 -103,10 +103,10 @@@ public abstract class AbstractMarker ex
          }
  
          @Override
 -        public AbstractMarker prepare(ColumnSpecification receiver) throws 
InvalidRequestException
 +        public AbstractMarker prepare(String keyspace, ColumnSpecification 
receiver) throws InvalidRequestException
          {
              if (receiver.type instanceof CollectionType)
-                 throw new InvalidRequestException("Invalid IN relation on 
collection column");
+                 throw new InvalidRequestException("Collection columns do not 
support IN relations");
  
              return new Lists.Marker(bindIndex, makeInReceiver(receiver));
          }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/Constants.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/Cql.g
index 4c1f2dc,ceb2bde..57b61a5
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@@ -962,44 -894,82 +976,85 @@@ relationType returns [Relation.Type op
      ;
  
  relation[List<Relation> clauses]
-     : name=cident type=relationType t=term { $clauses.add(new Relation(name, 
type, t)); }
-     | K_TOKEN 
-         { List<ColumnIdentifier> l = new ArrayList<ColumnIdentifier>(); }
-           '(' name1=cident { l.add(name1); } ( ',' namen=cident { 
l.add(namen); })* ')'
-         type=relationType t=term
+     : name=cident type=relationType t=term { $clauses.add(new 
SingleColumnRelation(name, type, t)); }
+     | K_TOKEN l=tupleOfIdentifiers type=relationType t=term
          {
              for (ColumnIdentifier id : l)
-                 $clauses.add(new Relation(id, type, t, true));
+                 $clauses.add(new SingleColumnRelation(id, type, t, true));
          }
-     | name=cident K_IN { Term.Raw marker = null; } (QMARK { marker = 
newINBindVariables(null); } | ':' mid=cident { marker = 
newINBindVariables(mid); })
-         { $clauses.add(new Relation(name, Relation.Type.IN, marker)); }
-     | name=cident K_IN { Relation rel = Relation.createInRelation($name.id); }
-        '(' ( f1=term { rel.addInValue(f1); } (',' fN=term { 
rel.addInValue(fN); } )* )? ')' { $clauses.add(rel); }
+     | name=cident K_IN marker=inMarker
+         { $clauses.add(new SingleColumnRelation(name, Relation.Type.IN, 
marker)); }
+     | name=cident K_IN inValues=singleColumnInValues
+         { $clauses.add(SingleColumnRelation.createInRelation($name.id, 
inValues)); }
 +    | name=cident K_CONTAINS { Relation.Type rt = Relation.Type.CONTAINS; } 
(K_KEY { rt = Relation.Type.CONTAINS_KEY; })?
-         t=term { $clauses.add(new Relation(name, rt, t)); }
-     | {
-          List<ColumnIdentifier> ids = new ArrayList<ColumnIdentifier>();
-          List<Term.Raw> terms = new ArrayList<Term.Raw>();
-       }
-         '(' n1=cident { ids.add(n1); } (',' ni=cident { ids.add(ni); })* ')'
-         type=relationType
-         '(' t1=term { terms.add(t1); } (',' ti=term { terms.add(ti); })* ')'
-       {
-           if (type == Relation.Type.IN)
-               addRecognitionError("Cannot use IN relation with tuple 
notation");
-           if (ids.size() != terms.size())
-               addRecognitionError(String.format("Number of values (" + 
terms.size() + ") in tuple notation doesn't match the number of column names (" 
+ ids.size() + ")"));
-           else
-               for (int i = 0; i < ids.size(); i++)
-                   $clauses.add(new Relation(ids.get(i), type, terms.get(i), i 
== 0 ? null : ids.get(i-1)));
-       }
++        t=term { $clauses.add(new SingleColumnRelation(name, rt, t)); }
+     | ids=tupleOfIdentifiers
+       ( K_IN
+           ( '(' ')'
+               { $clauses.add(MultiColumnRelation.createInRelation(ids, new 
ArrayList<Tuples.Literal>())); }
+           | tupleInMarker=inMarkerForTuple /* (a, b, c) IN ? */
+               { 
$clauses.add(MultiColumnRelation.createSingleMarkerInRelation(ids, 
tupleInMarker)); }
+           | literals=tupleOfTupleLiterals /* (a, b, c) IN ((1, 2, 3), (4, 5, 
6), ...) */
+               {
+                   $clauses.add(MultiColumnRelation.createInRelation(ids, 
literals));
+               }
+           | markers=tupleOfMarkersForTuples /* (a, b, c) IN (?, ?, ...) */
+               { $clauses.add(MultiColumnRelation.createInRelation(ids, 
markers)); }
+           )
+       | type=relationType literal=tupleLiteral /* (a, b, c) > (1, 2, 3) or 
(a, b, c) > (?, ?, ?) */
+           {
+               $clauses.add(MultiColumnRelation.createNonInRelation(ids, type, 
literal));
+           }
+       | type=relationType tupleMarker=markerForTuple /* (a, b, c) >= ? */
+           { $clauses.add(MultiColumnRelation.createNonInRelation(ids, type, 
tupleMarker)); }
+       )
      | '(' relation[$clauses] ')'
      ;
  
+ inMarker returns [AbstractMarker.INRaw marker]
+     : QMARK { $marker = newINBindVariables(null); }
+     | ':' name=cident { $marker = newINBindVariables(name); }
+     ;
+ 
+ tupleOfIdentifiers returns [List<ColumnIdentifier> ids]
+     @init { $ids = new ArrayList<ColumnIdentifier>(); }
+     : '(' n1=cident { $ids.add(n1); } (',' ni=cident { $ids.add(ni); })* ')'
+     ;
+ 
+ singleColumnInValues returns [List<Term.Raw> terms]
+     @init { $terms = new ArrayList<Term.Raw>(); }
+     : '(' ( t1 = term { $terms.add(t1); } (',' ti=term { $terms.add(ti); })* 
)? ')'
+     ;
+ 
+ tupleLiteral returns [Tuples.Literal literal]
+     @init { List<Term.Raw> terms = new ArrayList<>(); }
+     : '(' t1=term { terms.add(t1); } (',' ti=term { terms.add(ti); })* ')' { 
$literal = new Tuples.Literal(terms); }
+     ;
+ 
+ tupleOfTupleLiterals returns [List<Tuples.Literal> literals]
+     @init { $literals = new ArrayList<>(); }
+     : '(' t1=tupleLiteral { $literals.add(t1); } (',' ti=tupleLiteral { 
$literals.add(ti); })* ')'
+     ;
+ 
+ markerForTuple returns [Tuples.Raw marker]
+     : QMARK { $marker = newTupleBindVariables(null); }
+     | ':' name=cident { $marker = newTupleBindVariables(name); }
+     ;
+ 
+ tupleOfMarkersForTuples returns [List<Tuples.Raw> markers]
+     @init { $markers = new ArrayList<Tuples.Raw>(); }
+     : '(' m1=markerForTuple { $markers.add(m1); } (',' mi=markerForTuple { 
$markers.add(mi); })* ')'
+     ;
+ 
+ inMarkerForTuple returns [Tuples.INRaw marker]
+     : QMARK { $marker = newTupleINBindVariables(null); }
+     | ':' name=cident { $marker = newTupleINBindVariables(name); }
+     ;
+ 
 -comparatorType returns [CQL3Type t]
 -    : c=native_type     { $t = c; }
 +comparatorType returns [CQL3Type.Raw t]
 +    : n=native_type     { $t = CQL3Type.Raw.from(n); }
      | c=collection_type { $t = c; }
 +    | id=userTypeName  { $t = CQL3Type.Raw.userType(id); }
      | s=STRING_LITERAL
        {
          try {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/Lists.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/Lists.java
index f12af88,d483dd5..c214fa8
--- a/src/java/org/apache/cassandra/cql3/Lists.java
+++ b/src/java/org/apache/cassandra/cql3/Lists.java
@@@ -147,25 -144,18 +147,30 @@@ public abstract class List
              }
          }
  
 -        public ByteBuffer get()
 +        public ByteBuffer get(QueryOptions options)
          {
 -            return CollectionType.pack(elements, elements.size());
 +            return CollectionSerializer.pack(elements, elements.size(), 
options.getProtocolVersion());
 +        }
 +
 +        public boolean equals(ListType lt, Value v)
 +        {
 +            if (elements.size() != v.elements.size())
 +                return false;
 +
 +            for (int i = 0; i < elements.size(); i++)
 +                if (lt.elements.compare(elements.get(i), v.elements.get(i)) 
!= 0)
 +                    return false;
 +
 +            return true;
          }
+ 
+         public List<ByteBuffer> getElements()
+         {
+             return elements;
+         }
      }
  
-     /*
+     /**
       * Basically similar to a Value, but with some non-pure function (that 
need
       * to be evaluated at execution time) in it.
       *
@@@ -223,11 -216,10 +231,10 @@@
              assert receiver.type instanceof ListType;
          }
  
 -        public Value bind(List<ByteBuffer> values) throws 
InvalidRequestException
 +        public Value bind(QueryOptions options) throws InvalidRequestException
          {
 -            ByteBuffer value = values.get(bindIndex);
 -            return value == null ? null : Value.fromSerialized(value, 
(ListType)receiver.type);
 +            ByteBuffer value = options.getValues().get(bindIndex);
 +            return value == null ? null : Value.fromSerialized(value, 
(ListType)receiver.type, options.getProtocolVersion());
- 
          }
      }
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/Relation.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/Relation.java
index 2eeef1d,0f1366d..5318907
--- a/src/java/org/apache/cassandra/cql3/Relation.java
+++ b/src/java/org/apache/cassandra/cql3/Relation.java
@@@ -17,78 -17,35 +17,47 @@@
   */
  package org.apache.cassandra.cql3;
  
- import java.util.ArrayList;
- import java.util.List;
 +
- /**
-  * Relations encapsulate the relationship between an entity of some kind, and
-  * a value (term). For example, <key> > "start" or "colname1" = "somevalue".
-  *
-  */
- public class Relation
- {
-     private final ColumnIdentifier entity;
-     private final Type relationType;
-     private final Term.Raw value;
-     private final List<Term.Raw> inValues;
-     public final boolean onToken;
+ public abstract class Relation {
  
-     // Will be null unless for tuple notations (#4851)
-     public final ColumnIdentifier previousInTuple;
+     protected Type relationType;
  
      public static enum Type
      {
 -        EQ, LT, LTE, GTE, GT, IN;
 +        EQ, LT, LTE, GTE, GT, IN, CONTAINS, CONTAINS_KEY;
 +
 +        public boolean allowsIndexQuery()
 +        {
 +            switch (this)
 +            {
 +                case EQ:
 +                case CONTAINS:
 +                case CONTAINS_KEY:
 +                    return true;
 +                default:
 +                    return false;
 +            }
 +        }
-     }
- 
-     private Relation(ColumnIdentifier entity, Type type, Term.Raw value, 
List<Term.Raw> inValues, boolean onToken, ColumnIdentifier previousInTuple)
-     {
-         this.entity = entity;
-         this.relationType = type;
-         this.value = value;
-         this.inValues = inValues;
-         this.onToken = onToken;
-         this.previousInTuple = previousInTuple;
-     }
  
-     /**
-      * Creates a new relation.
-      *
-      * @param entity the kind of relation this is; what the term is being 
compared to.
-      * @param type the type that describes how this entity relates to the 
value.
-      * @param value the value being compared.
-      */
-     public Relation(ColumnIdentifier entity, Type type, Term.Raw value)
-     {
-         this(entity, type, value, null, false, null);
-     }
- 
-     public Relation(ColumnIdentifier entity, Type type, Term.Raw value, 
boolean onToken)
-     {
-         this(entity, type, value, null, onToken, null);
-     }
- 
-     public Relation(ColumnIdentifier entity, Type type, Term.Raw value, 
ColumnIdentifier previousInTuple)
-     {
-         this(entity, type, value, null, false, previousInTuple);
-     }
- 
-     public static Relation createInRelation(ColumnIdentifier entity)
-     {
-         return new Relation(entity, Type.IN, null, new ArrayList<Term.Raw>(), 
false, null);
+         @Override
+         public String toString()
+         {
+             switch (this)
+             {
+                 case EQ:
+                     return "=";
+                 case LT:
+                     return "<";
+                 case LTE:
+                     return "<=";
+                 case GT:
+                     return ">";
+                 case GTE:
+                     return ">=";
 -                case IN:
 -                    return "IN";
+                 default:
+                     return this.name();
+             }
+         }
      }
  
      public Type operator()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/Term.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/Term.java
index de866e1,96b4b71..e5206c8
--- a/src/java/org/apache/cassandra/cql3/Term.java
+++ b/src/java/org/apache/cassandra/cql3/Term.java
@@@ -88,9 -88,14 +88,14 @@@ public interface Ter
           * case this RawTerm describe a list index or a map key, etc...
           * @return the prepared term.
           */
 -        public Term prepare(ColumnSpecification receiver) throws 
InvalidRequestException;
 +        public Term prepare(String keyspace, ColumnSpecification receiver) 
throws InvalidRequestException;
      }
  
+     public interface MultiColumnRaw extends Raw
+     {
 -        public Term prepare(List<? extends ColumnSpecification> receiver) 
throws InvalidRequestException;
++        public Term prepare(String keyspace, List<? extends 
ColumnSpecification> receiver) throws InvalidRequestException;
+     }
+ 
      /**
       * A terminal term, one that can be reduced to a byte buffer directly.
       *

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/Tuples.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/Tuples.java
index 0000000,9e86912..3aabbd3
mode 000000,100644..100644
--- a/src/java/org/apache/cassandra/cql3/Tuples.java
+++ b/src/java/org/apache/cassandra/cql3/Tuples.java
@@@ -1,0 -1,349 +1,349 @@@
+ /*
+  * 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 org.apache.cassandra.db.marshal.AbstractType;
+ import org.apache.cassandra.db.marshal.CollectionType;
+ import org.apache.cassandra.db.marshal.ListType;
+ import org.apache.cassandra.db.marshal.TupleType;
+ import org.apache.cassandra.exceptions.InvalidRequestException;
+ import org.apache.cassandra.serializers.MarshalException;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ 
+ import java.nio.ByteBuffer;
+ import java.util.*;
+ 
+ /**
+  * Static helper methods and classes for tuples.
+  */
+ public class Tuples
+ {
+     private static final Logger logger = 
LoggerFactory.getLogger(Tuples.class);
+ 
+     /**
+      * A raw, literal tuple.  When prepared, this will become a Tuples.Value 
or Tuples.DelayedValue, depending
+      * on whether the tuple holds NonTerminals.
+      */
+     public static class Literal implements Term.MultiColumnRaw
+     {
+         private final List<Term.Raw> elements;
+ 
+         public Literal(List<Term.Raw> elements)
+         {
+             this.elements = elements;
+         }
+ 
 -        public Term prepare(List<? extends ColumnSpecification> receivers) 
throws InvalidRequestException
++        public Term prepare(String keyspace, List<? extends 
ColumnSpecification> receivers) throws InvalidRequestException
+         {
+             if (elements.size() != receivers.size())
+                 throw new InvalidRequestException(String.format("Expected %d 
elements in value tuple, but got %d: %s", receivers.size(), elements.size(), 
this));
+ 
+             List<Term> values = new ArrayList<>(elements.size());
+             boolean allTerminal = true;
+             for (int i = 0; i < elements.size(); i++)
+             {
 -                Term t = elements.get(i).prepare(receivers.get(i));
++                Term t = elements.get(i).prepare(keyspace, receivers.get(i));
+                 if (t instanceof Term.NonTerminal)
+                     allTerminal = false;
+ 
+                 values.add(t);
+             }
+             DelayedValue value = new DelayedValue(values);
 -            return allTerminal ? 
value.bind(Collections.<ByteBuffer>emptyList()) : value;
++            return allTerminal ? value.bind(QueryOptions.DEFAULT) : value;
+         }
+ 
 -        public Term prepare(ColumnSpecification receiver)
++        public Term prepare(String keyspace, ColumnSpecification receiver)
+         {
+             throw new AssertionError("Tuples.Literal instances require a list 
of receivers for prepare()");
+         }
+ 
 -        public boolean isAssignableTo(ColumnSpecification receiver)
++        public boolean isAssignableTo(String keyspace, ColumnSpecification 
receiver)
+         {
+             // tuples shouldn't be assignable to anything right now
+             return false;
+         }
+ 
+         @Override
+         public String toString()
+         {
+             return tupleToString(elements);
+         }
+     }
+ 
+     /**
+      * A tuple of terminal values (e.g (123, 'abc')).
+      */
+     public static class Value extends Term.MultiItemTerminal
+     {
+         public final ByteBuffer[] elements;
+ 
+         public Value(ByteBuffer[] elements)
+         {
+             this.elements = elements;
+         }
+ 
+         public static Value fromSerialized(ByteBuffer bytes, TupleType type)
+         {
+             return new Value(type.split(bytes));
+         }
+ 
 -        public ByteBuffer get()
++        public ByteBuffer get(QueryOptions options)
+         {
+             throw new UnsupportedOperationException();
+         }
+ 
+         public List<ByteBuffer> getElements()
+         {
+             return Arrays.asList(elements);
+         }
+     }
+ 
+     /**
+      * Similar to Value, but contains at least one NonTerminal, such as a 
non-pure functions or bind marker.
+      */
+     public static class DelayedValue extends Term.NonTerminal
+     {
+         public final List<Term> elements;
+ 
+         public DelayedValue(List<Term> elements)
+         {
+             this.elements = elements;
+         }
+ 
+         public boolean containsBindMarker()
+         {
+             for (Term term : elements)
+                 if (term.containsBindMarker())
+                     return true;
+ 
+             return false;
+         }
+ 
+         public void collectMarkerSpecification(VariableSpecifications 
boundNames)
+         {
+             for (Term term : elements)
+                 term.collectMarkerSpecification(boundNames);
+         }
+ 
 -        public Value bind(List<ByteBuffer> values) throws 
InvalidRequestException
++        public Value bind(QueryOptions options) throws InvalidRequestException
+         {
+             ByteBuffer[] buffers = new ByteBuffer[elements.size()];
+             for (int i=0; i < elements.size(); i++)
+             {
 -                ByteBuffer bytes = elements.get(i).bindAndGet(values);
++                ByteBuffer bytes = elements.get(i).bindAndGet(options);
+                 if (bytes == null)
+                     throw new InvalidRequestException("Tuples may not contain 
null values");
+ 
 -                buffers[i] = elements.get(i).bindAndGet(values);
++                buffers[i] = elements.get(i).bindAndGet(options);
+             }
+             return new Value(buffers);
+         }
+ 
+         @Override
+         public String toString()
+         {
+             return tupleToString(elements);
+         }
+     }
+ 
+     /**
+      * A terminal value for a list of IN values that are tuples. For example: 
"SELECT ... WHERE (a, b, c) IN ?"
+      * This is similar to Lists.Value, but allows us to keep components of 
the tuples in the list separate.
+      */
+     public static class InValue extends Term.Terminal
+     {
+         List<List<ByteBuffer>> elements;
+ 
+         public InValue(List<List<ByteBuffer>> items)
+         {
+             this.elements = items;
+         }
+ 
+         public static InValue fromSerialized(ByteBuffer value, ListType type) 
throws InvalidRequestException
+         {
+             try
+             {
+                 // Collections have this small hack that validate cannot be 
called on a serialized object,
+                 // but compose does the validation (so we're fine).
+                 List<?> l = (List<?>)type.compose(value);
+ 
+                 assert type.elements instanceof TupleType;
+                 TupleType tupleType = (TupleType) type.elements;
+ 
+                 // type.split(bytes)
+                 List<List<ByteBuffer>> elements = new ArrayList<>(l.size());
+                 for (Object element : l)
+                     
elements.add(Arrays.asList(tupleType.split(type.elements.decompose(element))));
+                 return new InValue(elements);
+             }
+             catch (MarshalException e)
+             {
+                 throw new InvalidRequestException(e.getMessage());
+             }
+         }
+ 
 -        public ByteBuffer get()
++        public ByteBuffer get(QueryOptions options)
+         {
+             throw new UnsupportedOperationException();
+         }
+ 
+         public List<List<ByteBuffer>> getSplitValues()
+         {
+             return elements;
+         }
+     }
+ 
+     /**
+      * A raw placeholder for a tuple of values for different multiple 
columns, each of which may have a different type.
+      * For example, "SELECT ... WHERE (col1, col2) > ?".
+      */
+     public static class Raw extends AbstractMarker.Raw implements 
Term.MultiColumnRaw
+     {
+         public Raw(int bindIndex)
+         {
+             super(bindIndex);
+         }
+ 
+         private static ColumnSpecification makeReceiver(List<? extends 
ColumnSpecification> receivers) throws InvalidRequestException
+         {
+             List<AbstractType<?>> types = new ArrayList<>(receivers.size());
+             StringBuilder inName = new StringBuilder("(");
+             for (int i = 0; i < receivers.size(); i++)
+             {
+                 ColumnSpecification receiver = receivers.get(i);
+                 inName.append(receiver.name);
+                 if (i < receivers.size() - 1)
+                     inName.append(",");
+                 types.add(receiver.type);
+             }
+             inName.append(')');
+ 
+             ColumnIdentifier identifier = new 
ColumnIdentifier(inName.toString(), true);
+             TupleType type = new TupleType(types);
+             return new ColumnSpecification(receivers.get(0).ksName, 
receivers.get(0).cfName, identifier, type);
+         }
+ 
 -        public AbstractMarker prepare(List<? extends ColumnSpecification> 
receivers) throws InvalidRequestException
++        public AbstractMarker prepare(String keyspace, List<? extends 
ColumnSpecification> receivers) throws InvalidRequestException
+         {
+             return new Tuples.Marker(bindIndex, makeReceiver(receivers));
+         }
+ 
+         @Override
 -        public AbstractMarker prepare(ColumnSpecification receiver)
++        public AbstractMarker prepare(String keyspace, ColumnSpecification 
receiver)
+         {
+             throw new AssertionError("Tuples.Raw.prepare() requires a list of 
receivers");
+         }
+     }
+ 
+     /**
+      * A raw marker for an IN list of tuples, like "SELECT ... WHERE (a, b, 
c) IN ?"
+      */
+     public static class INRaw extends AbstractMarker.Raw
+     {
+         public INRaw(int bindIndex)
+         {
+             super(bindIndex);
+         }
+ 
+         private static ColumnSpecification makeInReceiver(List<? extends 
ColumnSpecification> receivers) throws InvalidRequestException
+         {
+             List<AbstractType<?>> types = new ArrayList<>(receivers.size());
+             StringBuilder inName = new StringBuilder("in(");
+             for (int i = 0; i < receivers.size(); i++)
+             {
+                 ColumnSpecification receiver = receivers.get(i);
+                 inName.append(receiver.name);
+                 if (i < receivers.size() - 1)
+                     inName.append(",");
+ 
+                 if (receiver.type instanceof CollectionType)
+                     throw new InvalidRequestException("Collection columns do 
not support IN relations");
+                 types.add(receiver.type);
+             }
+             inName.append(')');
+ 
+             ColumnIdentifier identifier = new 
ColumnIdentifier(inName.toString(), true);
+             TupleType type = new TupleType(types);
+             return new ColumnSpecification(receivers.get(0).ksName, 
receivers.get(0).cfName, identifier, ListType.getInstance(type));
+         }
+ 
 -        public AbstractMarker prepare(List<? extends ColumnSpecification> 
receivers) throws InvalidRequestException
++        public AbstractMarker prepare(String keyspace, List<? extends 
ColumnSpecification> receivers) throws InvalidRequestException
+         {
+             return new InMarker(bindIndex, makeInReceiver(receivers));
+         }
+ 
+         @Override
 -        public AbstractMarker prepare(ColumnSpecification receiver)
++        public AbstractMarker prepare(String keyspace, ColumnSpecification 
receiver)
+         {
+             throw new AssertionError("Tuples.INRaw.prepare() requires a list 
of receivers");
+         }
+     }
+ 
+     /**
+      * Represents a marker for a single tuple, like "SELECT ... WHERE (a, b, 
c) > ?"
+      */
+     public static class Marker extends AbstractMarker
+     {
+         public Marker(int bindIndex, ColumnSpecification receiver)
+         {
+             super(bindIndex, receiver);
+         }
+ 
 -        public Value bind(List<ByteBuffer> values) throws 
InvalidRequestException
++        public Value bind(QueryOptions options) throws InvalidRequestException
+         {
 -            ByteBuffer value = values.get(bindIndex);
++            ByteBuffer value = options.getValues().get(bindIndex);
+             if (value == null)
+                 return null;
+ 
+             return value == null ? null : Value.fromSerialized(value, 
(TupleType)receiver.type);
+         }
+     }
+ 
+     /**
+      * Represents a marker for a set of IN values that are tuples, like 
"SELECT ... WHERE (a, b, c) IN ?"
+      */
+     public static class InMarker extends AbstractMarker
+     {
+         protected InMarker(int bindIndex, ColumnSpecification receiver)
+         {
+             super(bindIndex, receiver);
+             assert receiver.type instanceof ListType;
+         }
+ 
 -        public InValue bind(List<ByteBuffer> values) throws 
InvalidRequestException
++        public InValue bind(QueryOptions options) throws 
InvalidRequestException
+         {
 -            ByteBuffer value = values.get(bindIndex);
++            ByteBuffer value = options.getValues().get(bindIndex);
+             return value == null ? null : InValue.fromSerialized(value, 
(ListType)receiver.type);
+         }
+     }
+ 
+     public static String tupleToString(List<?> items)
+     {
+ 
+         StringBuilder sb = new StringBuilder("(");
+         for (int i = 0; i < items.size(); i++)
+         {
+             sb.append(items.get(i));
+             if (i < items.size() - 1)
+                 sb.append(", ");
+         }
+         sb.append(')');
+         return sb.toString();
+     }
+ }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index ad88eaf,11aa0b1..621006b
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@@ -29,10 -31,10 +29,12 @@@ import org.apache.cassandra.config.CFMe
  import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.cql3.*;
  import org.apache.cassandra.db.*;
 +import org.apache.cassandra.db.composites.CBuilder;
 +import org.apache.cassandra.db.composites.Composite;
  import org.apache.cassandra.db.filter.ColumnSlice;
  import org.apache.cassandra.db.filter.SliceQueryFilter;
+ import org.apache.cassandra.db.marshal.CompositeType;
+ import org.apache.cassandra.db.marshal.UTF8Type;
  import org.apache.cassandra.db.marshal.BooleanType;
  import org.apache.cassandra.exceptions.*;
  import org.apache.cassandra.service.ClientState;
@@@ -218,46 -237,54 +220,53 @@@ public abstract class ModificationState
          return ifExists;
      }
  
 -    private void addKeyValues(CFDefinition.Name name, Restriction values) 
throws InvalidRequestException
 +    private void addKeyValues(ColumnDefinition def, Restriction values) 
throws InvalidRequestException
      {
 -        if (name.kind == CFDefinition.Name.Kind.COLUMN_ALIAS)
 +        if (def.kind == ColumnDefinition.Kind.CLUSTERING_COLUMN)
              hasNoClusteringColumns = false;
 -        if (processedKeys.put(name.name, values) != null)
 -            throw new InvalidRequestException(String.format("Multiple 
definitions found for PRIMARY KEY part %s", name.name));
 +        if (processedKeys.put(def.name, values) != null)
 +            throw new InvalidRequestException(String.format("Multiple 
definitions found for PRIMARY KEY part %s", def.name));
      }
  
 -    public void addKeyValue(CFDefinition.Name name, Term value) throws 
InvalidRequestException
 +    public void addKeyValue(ColumnDefinition def, Term value) throws 
InvalidRequestException
      {
-         addKeyValues(def, new Restriction.EQ(value, false));
 -        addKeyValues(name, new SingleColumnRestriction.EQ(value, false));
++        addKeyValues(def, new SingleColumnRestriction.EQ(value, false));
      }
  
      public void processWhereClause(List<Relation> whereClause, 
VariableSpecifications names) throws InvalidRequestException
      {
-         for (Relation rel : whereClause)
 -        CFDefinition cfDef = cfm.getCfDef();
+         for (Relation relation : whereClause)
          {
 -            if (!(relation instanceof SingleColumnRelation))
++            if (relation.isMultiColumn())
+             {
+                 throw new InvalidRequestException(
+                         String.format("Multi-column relations cannot be used 
in WHERE clauses for modification statements: %s", relation));
+             }
+             SingleColumnRelation rel = (SingleColumnRelation) relation;
+ 
 -            CFDefinition.Name name = cfDef.get(rel.getEntity());
 -            if (name == null)
 +            ColumnDefinition def = cfm.getColumnDefinition(rel.getEntity());
 +            if (def == null)
                  throw new InvalidRequestException(String.format("Unknown key 
identifier %s", rel.getEntity()));
  
 -            switch (name.kind)
 +            switch (def.kind)
              {
 -                case KEY_ALIAS:
 -                case COLUMN_ALIAS:
 +                case PARTITION_KEY:
 +                case CLUSTERING_COLUMN:
                      Restriction restriction;
  
                      if (rel.operator() == Relation.Type.EQ)
                      {
 -                        Term t = rel.getValue().prepare(name);
 +                        Term t = rel.getValue().prepare(keyspace(), def);
                          t.collectMarkerSpecification(names);
-                         restriction = new Restriction.EQ(t, false);
+                         restriction = new SingleColumnRestriction.EQ(t, 
false);
                      }
 -                    else if (name.kind == CFDefinition.Name.Kind.KEY_ALIAS && 
rel.operator() == Relation.Type.IN)
 +                    else if (def.kind == ColumnDefinition.Kind.PARTITION_KEY 
&& rel.operator() == Relation.Type.IN)
                      {
                          if (rel.getValue() != null)
                          {
 -                            Term t = rel.getValue().prepare(name);
 +                            Term t = rel.getValue().prepare(keyspace(), def);
                              t.collectMarkerSpecification(names);
-                             restriction = Restriction.IN.create(t);
+                             restriction = new 
SingleColumnRestriction.InWithMarker((Lists.Marker)t);
                          }
                          else
                          {
@@@ -638,9 -689,9 +647,8 @@@
      /**
       * Convert statement into a list of mutations to apply on the server
       *
 -     * @param variables value for prepared statement markers
 +     * @param options value for prepared statement markers
       * @param local if true, any requests (for collections) performed by 
getMutation should be done locally only.
--     * @param cl the consistency to use for the potential reads involved in 
generating the mutations (for lists set/delete operations)
       * @param now the current timestamp in microseconds to use if no 
timestamp is user provided.
       *
       * @return list of the mutations

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/statements/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/MultiColumnRestriction.java
index 0000000,f643684..96cb905
mode 000000,100644..100644
--- a/src/java/org/apache/cassandra/cql3/statements/MultiColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/statements/MultiColumnRestriction.java
@@@ -1,0 -1,135 +1,137 @@@
+ /*
+  * 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.statements;
+ 
+ import org.apache.cassandra.cql3.AbstractMarker;
++import org.apache.cassandra.cql3.QueryOptions;
+ import org.apache.cassandra.cql3.Term;
+ import org.apache.cassandra.cql3.Tuples;
+ import org.apache.cassandra.exceptions.InvalidRequestException;
+ 
+ import java.nio.ByteBuffer;
+ import java.util.ArrayList;
+ import java.util.List;
+ 
+ public interface MultiColumnRestriction extends Restriction
+ {
+     public static class EQ extends SingleColumnRestriction.EQ implements 
MultiColumnRestriction
+     {
+         public EQ(Term value, boolean onToken)
+         {
+             super(value, onToken);
+         }
+ 
+         public boolean isMultiColumn()
+         {
+             return true;
+         }
+ 
 -        public List<ByteBuffer> values(List<ByteBuffer> variables) throws 
InvalidRequestException
++        public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException
+         {
 -            Tuples.Value t = (Tuples.Value)value.bind(variables);
++            Tuples.Value t = (Tuples.Value)value.bind(options);
+             return t.getElements();
+         }
+     }
+ 
+     public interface IN extends MultiColumnRestriction
+     {
 -        public List<List<ByteBuffer>> splitValues(List<ByteBuffer> variables) 
throws InvalidRequestException;
++        public List<List<ByteBuffer>> splitValues(QueryOptions options) 
throws InvalidRequestException;
+     }
+ 
+     /**
+      * An IN restriction that has a set of terms for in values.
+      * For example: "SELECT ... WHERE (a, b, c) IN ((1, 2, 3), (4, 5, 6))" or 
"WHERE (a, b, c) IN (?, ?)"
+      */
+     public static class InWithValues extends 
SingleColumnRestriction.InWithValues implements MultiColumnRestriction.IN
+     {
+         public InWithValues(List<Term> values)
+         {
+             super(values);
+         }
+ 
+         public boolean isMultiColumn()
+         {
+             return true;
+         }
+ 
 -        public List<List<ByteBuffer>> splitValues(List<ByteBuffer> variables) 
throws InvalidRequestException
++        public List<List<ByteBuffer>> splitValues(QueryOptions options) 
throws InvalidRequestException
+         {
+             List<List<ByteBuffer>> buffers = new ArrayList<>(values.size());
+             for (Term value : values)
+             {
 -                Term.MultiItemTerminal term = 
(Term.MultiItemTerminal)value.bind(variables);
++                Term.MultiItemTerminal term = 
(Term.MultiItemTerminal)value.bind(options);
+                 buffers.add(term.getElements());
+             }
+             return buffers;
+         }
+     }
+ 
+     /**
+      * An IN restriction that uses a single marker for a set of IN values 
that are tuples.
+      * For example: "SELECT ... WHERE (a, b, c) IN ?"
+      */
+     public static class InWithMarker extends 
SingleColumnRestriction.InWithMarker implements MultiColumnRestriction.IN
+     {
+         public InWithMarker(AbstractMarker marker)
+         {
+             super(marker);
+         }
+ 
+         public boolean isMultiColumn()
+         {
+             return true;
+         }
+ 
 -        public List<List<ByteBuffer>> splitValues(List<ByteBuffer> variables) 
throws InvalidRequestException
++        public List<List<ByteBuffer>> splitValues(QueryOptions options) 
throws InvalidRequestException
+         {
 -            Tuples.InValue inValue = ((Tuples.InMarker) 
marker).bind(variables);
++            Tuples.InMarker inMarker = (Tuples.InMarker)marker;
++            Tuples.InValue inValue = inMarker.bind(options);
+             if (inValue == null)
+                 throw new InvalidRequestException("Invalid null value for IN 
restriction");
+             return inValue.getSplitValues();
+         }
+     }
+ 
+     public static class Slice extends SingleColumnRestriction.Slice 
implements MultiColumnRestriction
+     {
+         public Slice(boolean onToken)
+         {
+             super(onToken);
+         }
+ 
+         public boolean isMultiColumn()
+         {
+             return true;
+         }
+ 
 -        public ByteBuffer bound(Bound b, List<ByteBuffer> variables) throws 
InvalidRequestException
++        public ByteBuffer bound(Bound b, QueryOptions options) throws 
InvalidRequestException
+         {
+             throw new UnsupportedOperationException("Multicolumn slice 
restrictions do not support bound()");
+         }
+ 
+         /**
+          * Similar to bounds(), but returns one ByteBuffer per-component in 
the bound instead of a single
+          * ByteBuffer to represent the entire bound.
+          */
 -        public List<ByteBuffer> componentBounds(Bound b, List<ByteBuffer> 
variables) throws InvalidRequestException
++        public List<ByteBuffer> componentBounds(Bound b, QueryOptions 
options) throws InvalidRequestException
+         {
 -            Tuples.Value value = (Tuples.Value)bounds[b.idx].bind(variables);
++            Tuples.Value value = (Tuples.Value)bounds[b.idx].bind(options);
+             return value.getElements();
+         }
+     }
+ }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bf521900/src/java/org/apache/cassandra/cql3/statements/Restriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/Restriction.java
index 4fd02c1,3d33bde..c529a38
--- a/src/java/org/apache/cassandra/cql3/statements/Restriction.java
+++ b/src/java/org/apache/cassandra/cql3/statements/Restriction.java
@@@ -18,14 -18,10 +18,10 @@@
  package org.apache.cassandra.cql3.statements;
  
  import java.nio.ByteBuffer;
- import java.util.ArrayList;
- import java.util.Collections;
  import java.util.List;
  
- import com.google.common.base.Objects;
- 
  import org.apache.cassandra.exceptions.InvalidRequestException;
 -import org.apache.cassandra.thrift.IndexOperator;
 +import org.apache.cassandra.db.IndexExpression;
  import org.apache.cassandra.cql3.*;
  
  /**
@@@ -39,398 -35,34 +35,35 @@@ public interface Restrictio
      public boolean isSlice();
      public boolean isEQ();
      public boolean isIN();
 +    public boolean isContains();
+     public boolean isMultiColumn();
  
 -    // Only supported for EQ and IN, but it's convenient to have here
 -    public List<ByteBuffer> values(List<ByteBuffer> variables) throws 
InvalidRequestException;
 +    // Not supported by Slice, but it's convenient to have here
 +    public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException;
  
-     public static class EQ implements Restriction
-     {
-         private final Term value;
-         private final boolean onToken;
- 
-         public EQ(Term value, boolean onToken)
-         {
-             this.value = value;
-             this.onToken = onToken;
-         }
- 
-         public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException
-         {
-             return Collections.singletonList(value.bindAndGet(options));
-         }
- 
-         public boolean isSlice()
-         {
-             return false;
-         }
- 
-         public boolean isEQ()
-         {
-             return true;
-         }
- 
-         public boolean isIN()
-         {
-             return false;
-         }
- 
-         public boolean isContains()
-         {
-             return false;
-         }
+     public static interface EQ extends Restriction {}
  
-         public boolean isOnToken()
-         {
-             return onToken;
-         }
- 
-         @Override
-         public String toString()
-         {
-             return String.format("EQ(%s)%s", value, onToken ? "*" : "");
-         }
-     }
- 
-     public static abstract class IN implements Restriction
+     public static interface IN extends Restriction
      {
-         public static IN create(List<Term> values)
-         {
-             return new WithValues(values);
-         }
- 
-         public static IN create(Term value)
-         {
-             assert value instanceof Lists.Marker; // we shouldn't have got 
there otherwise
-             return new WithMarker((Lists.Marker)value);
-         }
- 
-         public boolean isSlice()
-         {
-             return false;
-         }
- 
-         public boolean isEQ()
-         {
-             return false;
-         }
- 
-         public boolean isContains()
-         {
-             return false;
-         }
- 
-         public boolean isIN()
-         {
-             return true;
-         }
- 
-         // Used when we need to know if it's a IN with just one value before 
we have
-         // the bind variables. This is ugly and only there for backward 
compatiblity
-         // because we used to treate IN with 1 value like an EQ and need to 
preserve
-         // this behavior.
-         public abstract boolean canHaveOnlyOneValue();
- 
-         public boolean isOnToken()
-         {
-             return false;
-         }
- 
-         private static class WithValues extends IN
-         {
-             private final List<Term> values;
- 
-             private WithValues(List<Term> values)
-             {
-                 this.values = values;
-             }
- 
-             public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException
-             {
-                 List<ByteBuffer> buffers = new 
ArrayList<ByteBuffer>(values.size());
-                 for (Term value : values)
-                     buffers.add(value.bindAndGet(options));
-                 return buffers;
-             }
- 
-             public boolean canHaveOnlyOneValue()
-             {
-                 return values.size() == 1;
-             }
- 
-             @Override
-             public String toString()
-             {
-                 return String.format("IN(%s)", values);
-             }
-         }
- 
-         private static class WithMarker extends IN
-         {
-             private final Lists.Marker marker;
- 
-             private WithMarker(Lists.Marker marker)
-             {
-                 this.marker = marker;
-             }
- 
-             public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException
-             {
-                 Lists.Value lval = marker.bind(options);
-                 if (lval == null)
-                     throw new InvalidRequestException("Invalid null value for 
IN restriction");
-                 return lval.elements;
-             }
- 
-             public boolean canHaveOnlyOneValue()
-             {
-                 return false;
-             }
- 
-             @Override
-             public String toString()
-             {
-                 return "IN ?";
-             }
-         }
+         public boolean canHaveOnlyOneValue();
      }
  
-     public static class Slice implements Restriction
+     public static interface Slice extends Restriction
      {
-         private final Term[] bounds;
-         private final boolean[] boundInclusive;
-         private final boolean onToken;
- 
-         // The name of the column that was preceding this one if the tuple 
notation of #4851 was used
-         // (note: if it is set for both bound, we'll validate both have the 
same previous value, but we
-         // still need to distinguish if it's set or not for both bound)
-         private final ColumnIdentifier[] previous;
- 
-         public Slice(boolean onToken)
-         {
-             this.bounds = new Term[2];
-             this.boundInclusive = new boolean[2];
-             this.previous = new ColumnIdentifier[2];
-             this.onToken = onToken;
-         }
- 
-         public boolean isSlice()
-         {
-             return true;
-         }
- 
-         public boolean isEQ()
-         {
-             return false;
-         }
- 
-         public boolean isIN()
-         {
-             return false;
-         }
- 
-         public boolean isContains()
-         {
-             return false;
-         }
- 
-         public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException
-         {
-             throw new UnsupportedOperationException();
-         }
- 
-         public boolean isOnToken()
-         {
-             return onToken;
-         }
- 
-         public boolean hasBound(Bound b)
-         {
-             return bounds[b.idx] != null;
-         }
- 
-         public ByteBuffer bound(Bound b, QueryOptions options) throws 
InvalidRequestException
-         {
-             return bounds[b.idx].bindAndGet(options);
-         }
- 
-         public boolean isInclusive(Bound b)
-         {
-             return bounds[b.idx] == null || boundInclusive[b.idx];
-         }
- 
-         public Relation.Type getRelation(Bound eocBound, Bound inclusiveBound)
-         {
-             switch (eocBound)
-             {
-                 case START:
-                     return boundInclusive[inclusiveBound.idx] ? 
Relation.Type.GTE : Relation.Type.GT;
-                 case END:
-                     return boundInclusive[inclusiveBound.idx] ? 
Relation.Type.LTE : Relation.Type.LT;
-             }
-             throw new AssertionError();
-         }
- 
-         public IndexExpression.Operator getIndexOperator(Bound b)
-         {
-             switch (b)
-             {
-                 case START:
-                     return boundInclusive[b.idx] ? 
IndexExpression.Operator.GTE : IndexExpression.Operator.GT;
-                 case END:
-                     return boundInclusive[b.idx] ? 
IndexExpression.Operator.LTE : IndexExpression.Operator.LT;
-             }
-             throw new AssertionError();
-         }
- 
-         public void setBound(ColumnIdentifier name, Relation.Type type, Term 
t, ColumnIdentifier previousName) throws InvalidRequestException
-         {
-             Bound b;
-             boolean inclusive;
-             switch (type)
-             {
-                 case GT:
-                     b = Bound.START;
-                     inclusive = false;
-                     break;
-                 case GTE:
-                     b = Bound.START;
-                     inclusive = true;
-                     break;
-                 case LT:
-                     b = Bound.END;
-                     inclusive = false;
-                     break;
-                 case LTE:
-                     b = Bound.END;
-                     inclusive = true;
-                     break;
-                 default:
-                     throw new AssertionError();
-             }
- 
-             if (bounds[b.idx] != null)
-                 throw new InvalidRequestException(String.format("Invalid 
restrictions found on %s", name));
- 
-             bounds[b.idx] = t;
-             boundInclusive[b.idx] = inclusive;
- 
-             // If a bound is part of a tuple notation (#4851), the other 
bound must either also be or must not be set at all,
-             // and this even if there is a 2ndary index (it's not supported 
by the 2ndary code). And it's easier to validate
-             // this here so we do.
-             Bound reverse = Bound.reverse(b);
-             if (hasBound(reverse) && !(Objects.equal(previousName, 
previous[reverse.idx])))
-                 throw new InvalidRequestException(String.format("Clustering 
column %s cannot be restricted both inside a tuple notation and outside it", 
name));
- 
-             previous[b.idx] = previousName;
-         }
- 
-         public boolean isPartOfTuple()
-         {
-             return previous[Bound.START.idx] != null || 
previous[Bound.END.idx] != null;
-         }
- 
-         @Override
-         public String toString()
-         {
-             return String.format("SLICE(%s %s, %s %s)%s", boundInclusive[0] ? 
">=" : ">",
-                                                           bounds[0],
-                                                           boundInclusive[1] ? 
"<=" : "<",
-                                                           bounds[1],
-                                                           onToken ? "*" : "");
-         }
-     }
- 
-     // This holds both CONTAINS and CONTAINS_KEY restriction because we might 
want to have both of them.
-     public static class Contains implements Restriction
-     {
-         private List<Term> values; // for CONTAINS
-         private List<Term> keys;   // for CONTAINS_KEY
- 
-         public boolean hasContains()
-         {
-             return values != null;
-         }
- 
-         public boolean hasContainsKey()
-         {
-             return keys != null;
-         }
- 
-         public void add(Term t, boolean isKey)
-         {
-             if (isKey)
-                 addKey(t);
-             else
-                 addValue(t);
-         }
- 
-         public void addValue(Term t)
-         {
-             if (values == null)
-                 values = new ArrayList<>();
-             values.add(t);
-         }
- 
-         public void addKey(Term t)
-         {
-             if (keys == null)
-                 keys = new ArrayList<>();
-             keys.add(t);
-         }
- 
-         public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException
-         {
-             if (values == null)
-                 return Collections.emptyList();
- 
-             List<ByteBuffer> buffers = new 
ArrayList<ByteBuffer>(values.size());
-             for (Term value : values)
-                 buffers.add(value.bindAndGet(options));
-             return buffers;
-         }
- 
-         public List<ByteBuffer> keys(QueryOptions options) throws 
InvalidRequestException
-         {
-             if (keys == null)
-                 return Collections.emptyList();
- 
-             List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(keys.size());
-             for (Term value : keys)
-                 buffers.add(value.bindAndGet(options));
-             return buffers;
-         }
- 
-         public boolean isSlice()
-         {
-             return false;
-         }
 -        public List<ByteBuffer> values(List<ByteBuffer> variables) throws 
InvalidRequestException;
++        public List<ByteBuffer> values(QueryOptions options) throws 
InvalidRequestException;
  
-         public boolean isEQ()
-         {
-             return false;
-         }
+         /** Returns true if the start or end bound (depending on the 
argument) is set, false otherwise */
+         public boolean hasBound(Bound b);
  
-         public boolean isIN()
-         {
-             return false;
-         }
 -        public ByteBuffer bound(Bound b, List<ByteBuffer> variables) throws 
InvalidRequestException;
++        public ByteBuffer bound(Bound b, QueryOptions options) throws 
InvalidRequestException;
  
-         public boolean isContains()
-         {
-             return true;
-         }
+         /** Returns true if the start or end bound (depending on the 
argument) is inclusive, false otherwise */
+         public boolean isInclusive(Bound b);
  
-         public boolean isOnToken()
-         {
-             return false;
-         }
+         public Relation.Type getRelation(Bound eocBound, Bound 
inclusiveBound);
  
 -        public IndexOperator getIndexOperator(Bound b);
++        public IndexExpression.Operator getIndexOperator(Bound b);
  
-         @Override
-         public String toString()
-         {
-             return String.format("CONTAINS(values=%s, keys=%s)", values, 
keys);
-         }
 -        public void setBound(Relation.Type type, Term t) throws 
InvalidRequestException;
++        public void setBound(ColumnIdentifier name, Relation.Type type, Term 
t) throws InvalidRequestException;
      }
  }

Reply via email to