You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sl...@apache.org on 2013/09/19 12:58:29 UTC

git commit: Support named bind variables in CQL

Updated Branches:
  refs/heads/cassandra-2.0 a0abadfdb -> 37e9bce38


Support named bind variables in CQL

patch by slebresne; reviewed by iamaleksey for CASSANDRA-6033


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

Branch: refs/heads/cassandra-2.0
Commit: 37e9bce381b9daf8e544db475e1a31d70e471e7b
Parents: a0abadf
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Thu Sep 19 12:57:48 2013 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Thu Sep 19 12:57:48 2013 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 doc/cql3/CQL.textile                            |  6 ++-
 doc/native_protocol_v2.spec                     | 20 +++++---
 .../apache/cassandra/cql3/AbstractMarker.java   |  4 +-
 .../org/apache/cassandra/cql3/Attributes.java   |  2 +-
 src/java/org/apache/cassandra/cql3/Cql.g        | 31 +++++++++---
 src/java/org/apache/cassandra/cql3/Lists.java   |  4 +-
 src/java/org/apache/cassandra/cql3/Maps.java    |  4 +-
 .../org/apache/cassandra/cql3/Operation.java    |  2 +-
 src/java/org/apache/cassandra/cql3/Sets.java    |  2 +-
 src/java/org/apache/cassandra/cql3/Term.java    |  6 +--
 .../cassandra/cql3/VariableSpecifications.java  | 52 ++++++++++++++++++++
 .../cassandra/cql3/functions/FunctionCall.java  |  9 +---
 .../cql3/statements/BatchStatement.java         |  4 +-
 .../cql3/statements/CreateTableStatement.java   |  1 -
 .../cql3/statements/DeleteStatement.java        |  4 +-
 .../cql3/statements/ModificationStatement.java  | 10 ++--
 .../cql3/statements/ParsedStatement.java        | 15 ++++--
 .../statements/SchemaAlteringStatement.java     |  5 ++
 .../cql3/statements/SelectStatement.java        |  8 +--
 .../cql3/statements/TruncateStatement.java      |  5 ++
 .../cql3/statements/UpdateStatement.java        |  8 +--
 .../cassandra/cql3/statements/UseStatement.java |  5 ++
 23 files changed, 150 insertions(+), 58 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index d101c74..b2fa36e 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -26,6 +26,7 @@
  * Support variadic parameters for IN clauses (CASSANDRA-4210)
  * cqlsh: return the result of CAS writes (CASSANDRA-5796)
  * Fix validation of IN clauses with 2ndary indexes (CASSANDRA-6050)
+ * Support named bind variables in CQL (CASSANDRA-6033)
 Merged from 1.2:
  * Avoid second-guessing out-of-space state (CASSANDRA-5605)
  * Tuning knobs for dealing with large blobs and many CFs (CASSANDRA-5982)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/doc/cql3/CQL.textile
----------------------------------------------------------------------
diff --git a/doc/cql3/CQL.textile b/doc/cql3/CQL.textile
index a880c08..ee0d700 100644
--- a/doc/cql3/CQL.textile
+++ b/doc/cql3/CQL.textile
@@ -96,6 +96,7 @@ bc(syntax)..
                | <boolean>
                | <hex>
   <variable> ::= '?'
+               | ':' <identifier>
       <term> ::= <constant>
                | <collection-literal>
                | <variable>
@@ -115,7 +116,7 @@ bc(syntax)..
 p. 
 Please note that not every possible productions of the grammar above will be valid in practice. Most notably, @<variable>@ and nested @<collection-literal>@ are currently not allowed inside @<collection-literal>@.
 
-p. The question mark (@?@) of @<variable>@ is a bind variables for "prepared statements":#preparedStatement.
+p. A @<variable>@ can be either anonymous (a question mark (@?@)) or named (an identifier preceded by @:@). Both declare a bind variables for "prepared statements":#preparedStatement. The only difference between an anymous and a named variable is that a named one will be easier to refer to (how exactly depends on the client driver used).
 
 p. The @<properties>@ production is use by statement that create and alter keyspaces and tables. Each @<property>@ is either a _simple_ one, in which case it just has a value, or a _map_ one, in which case it's value is a map grouping sub-options. The following will refer to one or the other as the _kind_ (_simple_ or _map_) of the property.
 
@@ -128,7 +129,7 @@ h3(#preparedStatement). Prepared Statement
 
 CQL supports _prepared statements_. Prepared statement is an optimization that allows to parse a query only once but execute it multiple times with different concrete values.
 
-In a statement, each time a column value is expected (in the data manipulation and query statements), a bind variable marker (denoted by a @?@ symbol) can be used instead. A statement with bind variables must then be _prepared_. Once it has been prepared, it can executed by providing concrete values for the bind variables (values for bind variables must be provided in the order the bind variables are defined in the query string). The exact procedure to prepare a statement and execute a prepared statement depends on the CQL driver used and is beyond the scope of this document.
+In a statement, each time a column value is expected (in the data manipulation and query statements), a @<variable>@ (see above) can be used instead. A statement with bind variables must then be _prepared_. Once it has been prepared, it can executed by providing concrete values for the bind variables. The exact procedure to prepare a statement and execute a prepared statement depends on the CQL driver used and is beyond the scope of this document.
 
 
 h2(#dataDefinition). Data Definition
@@ -1090,6 +1091,7 @@ h3. 3.1.1
 
 * @SELECT@ statement now allows listing the partition keys (using the @DISTINCT@ modifier). See "CASSANDRA-4536":https://issues.apache.org/jira/browse/CASSANDRA-4536.
 * The syntax @c IN ?@ is now supported in @WHERE@ clauses. In that case, the value expected for the bind variable will be a list of whatever type @c@ is.
+* It is now possible to use named bind variables (using @:name@ instead of @?@).
 
 h3. 3.1.0
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/doc/native_protocol_v2.spec
----------------------------------------------------------------------
diff --git a/doc/native_protocol_v2.spec b/doc/native_protocol_v2.spec
index 8f081df..b0449bb 100644
--- a/doc/native_protocol_v2.spec
+++ b/doc/native_protocol_v2.spec
@@ -497,15 +497,21 @@ Table of Contents
           <flags>. If present, it is composed of two [string] representing the
           (unique) keyspace name and table name the columns return are of.
         - <col_spec_i> specifies the columns returned in the query. There is
-          <column_count> such column specification that are composed of:
-            (<ksname><tablename>)?<column_name><type>
+          <column_count> such column specifications that are composed of:
+            (<ksname><tablename>)?<name><type>
           The initial <ksname> and <tablename> are two [string] are only present
           if the Global_tables_spec flag is not set. The <column_name> is a
-          [string] and <type> is an [option] that correspond to the column name
-          and type. The option for <type> is either a native type (see below),
-          in which case the option has no value, or a 'custom' type, in which
-          case the value is a [string] representing the full qualified class
-          name of the type represented. Valid option ids are:
+          [string] and <type> is an [option] that correspond to the description
+          (what this description is depends a bit on the context: in results to
+          selects, this will be either the user chosen alias or the selection used
+          (often a colum name, but it can be a function call too). In results to
+          a PREPARE, this will be either the name of the bind variable corresponding
+          or the column name for the variable if it is "anonymous") and type of
+          the corresponding result. The option for <type> is either a native
+          type (see below), in which case the option has no value, or a
+          'custom' type, in which case the value is a [string] representing
+          the full qualified class name of the type represented. Valid option
+          ids are:
             0x0000    Custom: the value is a [string], see above.
             0x0001    Ascii
             0x0002    Bigint

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/AbstractMarker.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/AbstractMarker.java b/src/java/org/apache/cassandra/cql3/AbstractMarker.java
index 0de0153..165cb00 100644
--- a/src/java/org/apache/cassandra/cql3/AbstractMarker.java
+++ b/src/java/org/apache/cassandra/cql3/AbstractMarker.java
@@ -36,9 +36,9 @@ public abstract class AbstractMarker extends Term.NonTerminal
         this.receiver = receiver;
     }
 
-    public void collectMarkerSpecification(ColumnSpecification[] boundNames)
+    public void collectMarkerSpecification(VariableSpecifications boundNames)
     {
-        boundNames[bindIndex] = receiver;
+        boundNames.add(bindIndex, receiver);
     }
 
     public boolean containsBindMarker()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/Attributes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Attributes.java b/src/java/org/apache/cassandra/cql3/Attributes.java
index 360f54a..a92cc80 100644
--- a/src/java/org/apache/cassandra/cql3/Attributes.java
+++ b/src/java/org/apache/cassandra/cql3/Attributes.java
@@ -105,7 +105,7 @@ public class Attributes
         return ttl;
     }
 
-    public void collectMarkerSpecification(ColumnSpecification[] boundNames)
+    public void collectMarkerSpecification(VariableSpecifications boundNames)
     {
         if (timestamp != null)
             timestamp.collectMarkerSpecification(boundNames);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g
index 5e11b39..6fb0db4 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -50,8 +50,22 @@ options {
 }
 
 @members {
-    private List<String> recognitionErrors = new ArrayList<String>();
-    private int currentBindMarkerIdx = -1;
+    private final List<String> recognitionErrors = new ArrayList<String>();
+    private final List<ColumnIdentifier> bindVariables = new ArrayList<ColumnIdentifier>();
+
+    public AbstractMarker.Raw newBindVariables(ColumnIdentifier name)
+    {
+        AbstractMarker.Raw marker = new AbstractMarker.Raw(bindVariables.size());
+        bindVariables.add(name);
+        return marker;
+    }
+
+    public AbstractMarker.INRaw newINBindVariables(ColumnIdentifier name)
+    {
+        AbstractMarker.INRaw marker = new AbstractMarker.INRaw(bindVariables.size());
+        bindVariables.add(name);
+        return marker;
+    }
 
     public void displayRecognitionError(String[] tokenNames, RecognitionException e)
     {
@@ -170,7 +184,7 @@ query returns [ParsedStatement stmnt]
     ;
 
 cqlStatement returns [ParsedStatement stmt]
-    @after{ if (stmt != null) stmt.setBoundTerms(currentBindMarkerIdx + 1); }
+    @after{ if (stmt != null) stmt.setBoundVariables(bindVariables); }
     : st1= selectStatement             { $stmt = st1; }
     | st2= insertStatement             { $stmt = st2; }
     | st3= updateStatement             { $stmt = st3; }
@@ -759,13 +773,15 @@ value returns [Term.Raw value]
     : c=constant           { $value = c; }
     | l=collection_literal { $value = l; }
     | K_NULL               { $value = Constants.NULL_LITERAL; }
-    | QMARK                { $value = new AbstractMarker.Raw(++currentBindMarkerIdx); }
+    | ':' id=cident        { $value = newBindVariables(id); }
+    | QMARK                { $value = newBindVariables(null); }
     ;
 
 intValue returns [Term.Raw value]
     :
-    | t=INTEGER { $value = Constants.Literal.integer($t.text); }
-    | QMARK     { $value = new AbstractMarker.Raw(++currentBindMarkerIdx); }
+    | t=INTEGER     { $value = Constants.Literal.integer($t.text); }
+    | ':' id=cident { $value = newBindVariables(id); }
+    | QMARK         { $value = newBindVariables(null); }
     ;
 
 functionName returns [String s]
@@ -853,7 +869,8 @@ relation[List<Relation> clauses]
             for (ColumnIdentifier id : l)
                 $clauses.add(new Relation(id, type, t, true));
         }
-    | name=cident K_IN QMARK { $clauses.add(new Relation(name, Relation.Type.IN, new AbstractMarker.INRaw(++currentBindMarkerIdx))); }
+    | 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); }
     ;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/Lists.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Lists.java b/src/java/org/apache/cassandra/cql3/Lists.java
index 803e4c9..4ca5eb3 100644
--- a/src/java/org/apache/cassandra/cql3/Lists.java
+++ b/src/java/org/apache/cassandra/cql3/Lists.java
@@ -174,7 +174,7 @@ public abstract class Lists
             return false;
         }
 
-        public void collectMarkerSpecification(ColumnSpecification[] boundNames)
+        public void collectMarkerSpecification(VariableSpecifications boundNames)
         {
         }
 
@@ -289,7 +289,7 @@ public abstract class Lists
         }
 
         @Override
-        public void collectMarkerSpecification(ColumnSpecification[] boundNames)
+        public void collectMarkerSpecification(VariableSpecifications boundNames)
         {
             super.collectMarkerSpecification(boundNames);
             idx.collectMarkerSpecification(boundNames);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/Maps.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Maps.java b/src/java/org/apache/cassandra/cql3/Maps.java
index 8d5bf3d..30d796c 100644
--- a/src/java/org/apache/cassandra/cql3/Maps.java
+++ b/src/java/org/apache/cassandra/cql3/Maps.java
@@ -187,7 +187,7 @@ public abstract class Maps
             return false;
         }
 
-        public void collectMarkerSpecification(ColumnSpecification[] boundNames)
+        public void collectMarkerSpecification(VariableSpecifications boundNames)
         {
         }
 
@@ -261,7 +261,7 @@ public abstract class Maps
         }
 
         @Override
-        public void collectMarkerSpecification(ColumnSpecification[] boundNames)
+        public void collectMarkerSpecification(VariableSpecifications boundNames)
         {
             super.collectMarkerSpecification(boundNames);
             k.collectMarkerSpecification(boundNames);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/Operation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Operation.java b/src/java/org/apache/cassandra/cql3/Operation.java
index 2e16cea..d84f57b 100644
--- a/src/java/org/apache/cassandra/cql3/Operation.java
+++ b/src/java/org/apache/cassandra/cql3/Operation.java
@@ -69,7 +69,7 @@ public abstract class Operation
      * @param boundNames the list of column specification where to collect the
      * bind variables of this term in.
      */
-    public void collectMarkerSpecification(ColumnSpecification[] boundNames)
+    public void collectMarkerSpecification(VariableSpecifications boundNames)
     {
         if (t != null)
             t.collectMarkerSpecification(boundNames);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/Sets.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Sets.java b/src/java/org/apache/cassandra/cql3/Sets.java
index 9c3edf3..0fcb8bf 100644
--- a/src/java/org/apache/cassandra/cql3/Sets.java
+++ b/src/java/org/apache/cassandra/cql3/Sets.java
@@ -179,7 +179,7 @@ public abstract class Sets
             return false;
         }
 
-        public void collectMarkerSpecification(ColumnSpecification[] boundNames)
+        public void collectMarkerSpecification(VariableSpecifications boundNames)
         {
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/Term.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Term.java b/src/java/org/apache/cassandra/cql3/Term.java
index f2fd74e..d69fc33 100644
--- a/src/java/org/apache/cassandra/cql3/Term.java
+++ b/src/java/org/apache/cassandra/cql3/Term.java
@@ -35,10 +35,10 @@ public interface Term
      * Collects the column specification for the bind variables in this Term.
      * This is obviously a no-op if the term is Terminal.
      *
-     * @param boundNames the list of column specification where to collect the
+     * @param boundNames the variables specification where to collect the
      * bind variables of this term in.
      */
-    public void collectMarkerSpecification(ColumnSpecification[] boundNames);
+    public void collectMarkerSpecification(VariableSpecifications boundNames);
 
     /**
      * Bind the values in this term to the values contained in {@code values}.
@@ -107,7 +107,7 @@ public interface Term
      */
     public abstract class Terminal implements Term
     {
-        public void collectMarkerSpecification(ColumnSpecification[] boundNames) {}
+        public void collectMarkerSpecification(VariableSpecifications boundNames) {}
         public Terminal bind(List<ByteBuffer> values) { return this; }
 
         // While some NonTerminal may not have bind markers, no Term can be Terminal

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/VariableSpecifications.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/VariableSpecifications.java b/src/java/org/apache/cassandra/cql3/VariableSpecifications.java
new file mode 100644
index 0000000..ecdba6f
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/VariableSpecifications.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class VariableSpecifications
+{
+    private final List<ColumnIdentifier> variableNames;
+    private final ColumnSpecification[] specs;
+
+    public VariableSpecifications(List<ColumnIdentifier> variableNames)
+    {
+        this.variableNames = variableNames;
+        this.specs = new ColumnSpecification[variableNames.size()];
+    }
+
+    public int size()
+    {
+        return variableNames.size();
+    }
+
+    public List<ColumnSpecification> getSpecifications()
+    {
+        return Arrays.asList(specs);
+    }
+
+    public void add(int bindIndex, ColumnSpecification spec)
+    {
+        ColumnIdentifier name = variableNames.get(bindIndex);
+        // Use the user name, if there is one
+        if (name != null)
+            spec = new ColumnSpecification(spec.ksName, spec.cfName, name, spec.type);
+        specs[bindIndex] = spec;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
index a6b86a2..8db03e6 100644
--- a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
+++ b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java
@@ -21,12 +21,7 @@ import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.cassandra.cql3.ColumnSpecification;
-import org.apache.cassandra.cql3.Constants;
-import org.apache.cassandra.cql3.Lists;
-import org.apache.cassandra.cql3.Maps;
-import org.apache.cassandra.cql3.Sets;
-import org.apache.cassandra.cql3.Term;
+import 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;
@@ -45,7 +40,7 @@ public class FunctionCall extends Term.NonTerminal
         this.terms = terms;
     }
 
-    public void collectMarkerSpecification(ColumnSpecification[] boundNames)
+    public void collectMarkerSpecification(VariableSpecifications boundNames)
     {
         for (Term t : terms)
             t.collectMarkerSpecification(boundNames);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
index bf9e315..cb9ff59 100644
--- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
@@ -191,7 +191,7 @@ public class BatchStatement implements CQLStatement
 
         public ParsedStatement.Prepared prepare() throws InvalidRequestException
         {
-            ColumnSpecification[] boundNames = new ColumnSpecification[getBoundsTerms()];
+            VariableSpecifications boundNames = getBoundsVariables();
 
             List<ModificationStatement> statements = new ArrayList<ModificationStatement>(parsedStatements.size());
             for (ModificationStatement.Parsed parsed : parsedStatements)
@@ -212,7 +212,7 @@ public class BatchStatement implements CQLStatement
             Attributes prepAttrs = attrs.prepare("[batch]", "[batch]");
             prepAttrs.collectMarkerSpecification(boundNames);
 
-            return new ParsedStatement.Prepared(new BatchStatement(getBoundsTerms(), type, statements, prepAttrs), Arrays.<ColumnSpecification>asList(boundNames));
+            return new ParsedStatement.Prepared(new BatchStatement(boundNames.size(), type, statements, prepAttrs), boundNames);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
index d3b8bde..74f7570 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
@@ -196,7 +196,6 @@ public class CreateTableStatement extends SchemaAlteringStatement
             properties.validate();
 
             CreateTableStatement stmt = new CreateTableStatement(cfName, properties, ifNotExists);
-            stmt.setBoundTerms(getBoundsTerms());
 
             Map<ByteBuffer, CollectionType> definedCollections = null;
             for (Map.Entry<ColumnIdentifier, CQL3Type> entry : definitions.entrySet())

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
index 54a1034..4006bdb 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
@@ -103,9 +103,9 @@ public class DeleteStatement extends ModificationStatement
             this.whereClause = whereClause;
         }
 
-        protected ModificationStatement prepareInternal(CFDefinition cfDef, ColumnSpecification[] boundNames, Attributes attrs) throws InvalidRequestException
+        protected ModificationStatement prepareInternal(CFDefinition cfDef, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException
         {
-            DeleteStatement stmt = new DeleteStatement(getBoundsTerms(), cfDef.cfm, attrs);
+            DeleteStatement stmt = new DeleteStatement(boundNames.size(), cfDef.cfm, attrs);
 
             for (Operation.RawDeletion deletion : deletions)
             {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index 2f1785d..4470b51 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -146,7 +146,7 @@ public abstract class ModificationStatement implements CQLStatement
         addKeyValues(name, new Restriction.EQ(value, false));
     }
 
-    public void processWhereClause(List<Relation> whereClause, ColumnSpecification[] names) throws InvalidRequestException
+    public void processWhereClause(List<Relation> whereClause, VariableSpecifications names) throws InvalidRequestException
     {
         CFDefinition cfDef = cfm.getCfDef();
         for (Relation rel : whereClause)
@@ -565,12 +565,12 @@ public abstract class ModificationStatement implements CQLStatement
 
         public ParsedStatement.Prepared prepare() throws InvalidRequestException
         {
-            ColumnSpecification[] boundNames = new ColumnSpecification[getBoundsTerms()];
+            VariableSpecifications boundNames = getBoundsVariables();
             ModificationStatement statement = prepare(boundNames);
-            return new ParsedStatement.Prepared(statement, Arrays.<ColumnSpecification>asList(boundNames));
+            return new ParsedStatement.Prepared(statement, boundNames);
         }
 
-        public ModificationStatement prepare(ColumnSpecification[] boundNames) throws InvalidRequestException
+        public ModificationStatement prepare(VariableSpecifications boundNames) throws InvalidRequestException
         {
             CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace(), columnFamily());
             CFDefinition cfDef = metadata.getCfDef();
@@ -633,6 +633,6 @@ public abstract class ModificationStatement implements CQLStatement
             return stmt;
         }
 
-        protected abstract ModificationStatement prepareInternal(CFDefinition cfDef, ColumnSpecification[] boundNames, Attributes attrs) throws InvalidRequestException;
+        protected abstract ModificationStatement prepareInternal(CFDefinition cfDef, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException;
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java b/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
index ffcb7ae..ecf8a8a 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java
@@ -25,17 +25,17 @@ import org.apache.cassandra.exceptions.RequestValidationException;
 
 public abstract class ParsedStatement
 {
-    private int boundTerms;
+    private VariableSpecifications variables;
 
-    public int getBoundsTerms()
+    public VariableSpecifications getBoundsVariables()
     {
-        return boundTerms;
+        return variables;
     }
 
     // Used by the parser and preparable statement
-    public void setBoundTerms(int boundTerms)
+    public void setBoundVariables(List<ColumnIdentifier> boundNames)
     {
-        this.boundTerms = boundTerms;
+        this.variables = new VariableSpecifications(boundNames);
     }
 
     public abstract Prepared prepare() throws RequestValidationException;
@@ -51,6 +51,11 @@ public abstract class ParsedStatement
             this.boundNames = boundNames;
         }
 
+        public Prepared(CQLStatement statement, VariableSpecifications names)
+        {
+            this(statement, names.getSpecifications());
+        }
+
         public Prepared(CQLStatement statement)
         {
             this(statement, Collections.<ColumnSpecification>emptyList());

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
index 968be9f..f2904e4 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
@@ -44,6 +44,11 @@ public abstract class SchemaAlteringStatement extends CFStatement implements CQL
         this.isColumnFamilyLevel = true;
     }
 
+    public int getBoundsTerms()
+    {
+        return 0;
+    }
+
     @Override
     public void prepareKeyspace(ClientState state) throws InvalidRequestException
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 86dad85..ff6d4d3 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -1061,7 +1061,7 @@ public class SelectStatement implements CQLStatement
 
             CFDefinition cfDef = cfm.getCfDef();
 
-            ColumnSpecification[] names = new ColumnSpecification[getBoundsTerms()];
+            VariableSpecifications names = getBoundsVariables();
 
             // Select clause
             if (parameters.isCount && !selectClause.isEmpty())
@@ -1081,7 +1081,7 @@ public class SelectStatement implements CQLStatement
                 prepLimit.collectMarkerSpecification(names);
             }
 
-            SelectStatement stmt = new SelectStatement(cfDef, getBoundsTerms(), parameters, selection, prepLimit);
+            SelectStatement stmt = new SelectStatement(cfDef, names.size(), parameters, selection, prepLimit);
 
             /*
              * WHERE clause. For a given entity, rules are:
@@ -1392,7 +1392,7 @@ public class SelectStatement implements CQLStatement
                                                     + "If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING");
             }
 
-            return new ParsedStatement.Prepared(stmt, Arrays.<ColumnSpecification>asList(names));
+            return new ParsedStatement.Prepared(stmt, names);
         }
 
         private void validateDistinctSelection(Collection<CFDefinition.Name> requestedColumns, Collection<CFDefinition.Name> partitionKey)
@@ -1423,7 +1423,7 @@ public class SelectStatement implements CQLStatement
             return new ColumnSpecification(keyspace(), columnFamily(), new ColumnIdentifier("[limit]", true), Int32Type.instance);
         }
 
-        Restriction updateRestriction(CFDefinition.Name name, Restriction restriction, Relation newRel, ColumnSpecification[] boundNames) throws InvalidRequestException
+        Restriction updateRestriction(CFDefinition.Name name, Restriction restriction, Relation newRel, VariableSpecifications boundNames) throws InvalidRequestException
         {
             ColumnSpecification receiver = name;
             if (newRel.onToken)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
index b58d3fd..d5baedf 100644
--- a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
@@ -36,6 +36,11 @@ public class TruncateStatement extends CFStatement implements CQLStatement
         super(name);
     }
 
+    public int getBoundsTerms()
+    {
+        return 0;
+    }
+
     public Prepared prepare() throws InvalidRequestException
     {
         return new Prepared(this);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
index d27493f..2db24ea 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@ -124,9 +124,9 @@ public class UpdateStatement extends ModificationStatement
             this.columnValues = columnValues;
         }
 
-        protected ModificationStatement prepareInternal(CFDefinition cfDef, ColumnSpecification[] boundNames, Attributes attrs) throws InvalidRequestException
+        protected ModificationStatement prepareInternal(CFDefinition cfDef, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException
         {
-            UpdateStatement stmt = new UpdateStatement(getBoundsTerms(), cfDef.cfm, attrs);
+            UpdateStatement stmt = new UpdateStatement(boundNames.size(), cfDef.cfm, attrs);
 
             // Created from an INSERT
             if (stmt.isCounter())
@@ -194,9 +194,9 @@ public class UpdateStatement extends ModificationStatement
             this.whereClause = whereClause;
         }
 
-        protected ModificationStatement prepareInternal(CFDefinition cfDef, ColumnSpecification[] boundNames, Attributes attrs) throws InvalidRequestException
+        protected ModificationStatement prepareInternal(CFDefinition cfDef, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException
         {
-            UpdateStatement stmt = new UpdateStatement(getBoundsTerms(), cfDef.cfm, attrs);
+            UpdateStatement stmt = new UpdateStatement(boundNames.size(), cfDef.cfm, attrs);
 
             for (Pair<ColumnIdentifier, Operation.RawUpdate> entry : updates)
             {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/37e9bce3/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/UseStatement.java b/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
index 11f3078..db2435f 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UseStatement.java
@@ -34,6 +34,11 @@ public class UseStatement extends ParsedStatement implements CQLStatement
         this.keyspace = keyspace;
     }
 
+    public int getBoundsTerms()
+    {
+        return 0;
+    }
+
     public Prepared prepare() throws InvalidRequestException
     {
         return new Prepared(this);