Author: eevans
Date: Wed Dec 14 22:52:54 2011
New Revision: 1214520

URL: http://svn.apache.org/viewvc?rev=1214520&view=rev
Log:
CQL support for prepared statements

Patch by Rick Shaw; reviewed by eevans for CASSANDRA-2475

Modified:
    cassandra/trunk/interface/cassandra.thrift
    cassandra/trunk/src/java/org/apache/cassandra/cql/AbstractModification.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/BatchStatement.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/CQLStatement.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g
    
cassandra/trunk/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/DeleteStatement.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/SelectExpression.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/SelectStatement.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/Term.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/UpdateStatement.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/WhereClause.java
    cassandra/trunk/src/java/org/apache/cassandra/service/ClientState.java
    cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java

Modified: cassandra/trunk/interface/cassandra.thrift
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/interface/cassandra.thrift?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/interface/cassandra.thrift (original)
+++ cassandra/trunk/interface/cassandra.thrift Wed Dec 14 22:52:54 2011
@@ -46,7 +46,7 @@ namespace rb CassandraThrift
 #           for every edit that doesn't result in a change to major/minor.
 #
 # See the Semantic Versioning Specification (SemVer) http://semver.org.
-const string VERSION = "19.19.0"
+const string VERSION = "19.22.0"
 
 
 #
@@ -461,6 +461,12 @@ struct CqlResult {
     4: optional CqlMetadata schema
 }
 
+struct CqlPreparedResult {
+    1: required i32 itemId,
+    2: required i32 count
+}
+
+
 service Cassandra {
   # auth methods
   void login(1: required AuthenticationRequest auth_request) throws 
(1:AuthenticationException authnx, 2:AuthorizationException authzx),
@@ -683,4 +689,27 @@ service Cassandra {
             2:UnavailableException ue,
             3:TimedOutException te,
             4:SchemaDisagreementException sde)
+            
+            
+  /**
+   * Prepare a CQL (Cassandra Query Language) statement by compiling and 
returning
+   * - the type of CQL statement
+   * - an id token of the compiled CQL stored on the server side.
+   * - a count of the discovered bound markers in the statement 
+   */
+  CqlPreparedResult prepare_cql_query(1:required binary query, 2:required 
Compression compression)
+    throws (1:InvalidRequestException ire)
+
+             
+  /**
+   * Executes a prepared CQL (Cassandra Query Language) statement by passing 
an id token and  a list of variables
+   * to bind and returns a CqlResult containing the results.
+   */
+  CqlResult execute_prepared_cql_query(1:required i32 itemId, 2:required 
list<string> values)
+    throws (1:InvalidRequestException ire,
+            2:UnavailableException ue,
+            3:TimedOutException te,
+            4:SchemaDisagreementException sde)
+           
+
 }

Modified: 
cassandra/trunk/src/java/org/apache/cassandra/cql/AbstractModification.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/AbstractModification.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/AbstractModification.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/AbstractModification.java 
Wed Dec 14 22:52:54 2011
@@ -103,7 +103,7 @@ public abstract class AbstractModificati
      *
      * @throws InvalidRequestException on the wrong request
      */
-    public abstract List<IMutation> prepareRowMutations(String keyspace, 
ClientState clientState)
+    public abstract List<IMutation> prepareRowMutations(String keyspace, 
ClientState clientState, List<String> variables)
             throws org.apache.cassandra.thrift.InvalidRequestException;
 
     /**
@@ -117,6 +117,6 @@ public abstract class AbstractModificati
      *
      * @throws InvalidRequestException on the wrong request
      */
-    public abstract List<IMutation> prepareRowMutations(String keyspace, 
ClientState clientState, Long timestamp)
+    public abstract List<IMutation> prepareRowMutations(String keyspace, 
ClientState clientState, Long timestamp, List<String> variables)
             throws org.apache.cassandra.thrift.InvalidRequestException;
 }

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/BatchStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/BatchStatement.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/BatchStatement.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/BatchStatement.java Wed 
Dec 14 22:52:54 2011
@@ -76,12 +76,13 @@ public class BatchStatement
         return timeToLive;
     }
 
-    public List<IMutation> getMutations(String keyspace, ClientState 
clientState) throws InvalidRequestException
+    public List<IMutation> getMutations(String keyspace, ClientState 
clientState, List<String> variables)
+    throws InvalidRequestException
     {
         List<IMutation> batch = new LinkedList<IMutation>();
 
         for (AbstractModification statement : statements) {
-            batch.addAll(statement.prepareRowMutations(keyspace, clientState, 
timestamp));
+            batch.addAll(statement.prepareRowMutations(keyspace, clientState, 
timestamp, variables));
         }
 
         return batch;

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/CQLStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/CQLStatement.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/CQLStatement.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/CQLStatement.java Wed Dec 
14 22:52:54 2011
@@ -24,6 +24,7 @@ public class CQLStatement
 {
     public StatementType type;
     public Object statement;
+    public int boundTerms = 0;
     
     public CQLStatement(StatementType type, Object statement)
     {

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g Wed Dec 14 22:52:54 
2011
@@ -450,7 +450,7 @@ comparatorType
     ;
 
 term returns [Term item]
-    : ( t=K_KEY | t=STRING_LITERAL | t=INTEGER | t=UUID | t=IDENT | t=FLOAT) { 
$item = new Term($t.text, $t.type); }
+    : ( t=K_KEY | t=STRING_LITERAL | t=INTEGER | t=UUID | t=IDENT | t=FLOAT | 
t=QMARK) { $item = new Term($t.text, $t.type); }
     ;
 
 termList returns [List<Term> items]
@@ -597,6 +597,11 @@ RANGEOP
 INTEGER
     : '-'? DIGIT+
     ;
+    
+QMARK
+    : '?'
+    ;
+
 
 /* Normally a lexer only emits one token at a time, but ours is tricked out
  * to support multiple (see @lexer::members near the top of the grammar).

Modified: 
cassandra/trunk/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- 
cassandra/trunk/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
 (original)
+++ 
cassandra/trunk/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
 Wed Dec 14 22:52:54 2011
@@ -115,7 +115,7 @@ public class CreateColumnFamilyStatement
     }
     
     /** Perform validation of parsed params */
-    private void validate() throws InvalidRequestException
+    private void validate(List<String> variables) throws 
InvalidRequestException
     {
         // Column family name
         if (!name.matches("\\w+"))
@@ -174,7 +174,7 @@ public class CreateColumnFamilyStatement
 
         for (Map.Entry<Term, String> column : columns.entrySet())
         {
-            ByteBuffer name = column.getKey().getByteBuffer(comparator);
+            ByteBuffer name = column.getKey().getByteBuffer(comparator, 
variables);
 
             if (keyAlias != null && keyAlias.equals(name))
                 throw new InvalidRequestException("Invalid column name: "
@@ -271,9 +271,9 @@ public class CreateColumnFamilyStatement
      * @return a CFMetaData instance corresponding to the values parsed from 
this statement
      * @throws InvalidRequestException on failure to validate parsed parameters
      */
-    public CFMetaData getCFMetaData(String keyspace) throws 
InvalidRequestException
+    public CFMetaData getCFMetaData(String keyspace, List<String> variables) 
throws InvalidRequestException
     {
-        validate();
+        validate(variables);
 
         CFMetaData newCFMD;
         try
@@ -367,4 +367,10 @@ public class CreateColumnFamilyStatement
         }
         return result;
     }
+
+    public Map<Term, String> getColumns()
+    {
+        return columns;
+    }
+    
 }

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/DeleteStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/DeleteStatement.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/DeleteStatement.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/DeleteStatement.java Wed 
Dec 14 22:52:54 2011
@@ -59,20 +59,17 @@ public class DeleteStatement extends Abs
         return columns;
     }
 
-    /** {@inheritDoc} */
     public List<Term> getKeys()
     {
         return keys;
     }
 
-    /** {@inheritDoc} */
-    public List<IMutation> prepareRowMutations(String keyspace, ClientState 
clientState) throws InvalidRequestException
+    public List<IMutation> prepareRowMutations(String keyspace, ClientState 
clientState, List<String> variables) throws InvalidRequestException
     {
-        return prepareRowMutations(keyspace, clientState, null);
+        return prepareRowMutations(keyspace, clientState, null, variables);
     }
 
-    /** {@inheritDoc} */
-    public List<IMutation> prepareRowMutations(String keyspace, ClientState 
clientState, Long timestamp) throws InvalidRequestException
+    public List<IMutation> prepareRowMutations(String keyspace, ClientState 
clientState, Long timestamp, List<String> variables) throws 
InvalidRequestException
     {
         clientState.hasColumnFamilyAccess(columnFamily, Permission.WRITE);
         AbstractType<?> keyType = Schema.instance.getCFMetaData(keyspace, 
columnFamily).getKeyValidator();
@@ -81,20 +78,21 @@ public class DeleteStatement extends Abs
 
         for (Term key : keys)
         {
-            rowMutations.add(mutationForKey(key.getByteBuffer(keyType), 
keyspace, timestamp, clientState));
+            rowMutations.add(mutationForKey(key.getByteBuffer(keyType, 
variables), keyspace, timestamp, clientState,variables));
         }
 
         return rowMutations;
     }
 
-    /** {@inheritDoc} */
-    public RowMutation mutationForKey(ByteBuffer key, String keyspace, Long 
timestamp, ClientState clientState) throws InvalidRequestException
+    public RowMutation mutationForKey(ByteBuffer key, String keyspace, Long 
timestamp, ClientState clientState, List<String> variables)
+    throws InvalidRequestException
     {
         RowMutation rm = new RowMutation(keyspace, key);
 
         CFMetaData metadata = validateColumnFamily(keyspace, columnFamily);
         QueryProcessor.validateKeyAlias(metadata, keyName);
 
+        @SuppressWarnings("rawtypes")
         AbstractType comparator = metadata.getComparatorFor(null);
 
         if (columns.size() < 1)
@@ -107,7 +105,7 @@ public class DeleteStatement extends Abs
             // Delete specific columns
             for (Term column : columns)
             {
-                ByteBuffer columnName = column.getByteBuffer(comparator);
+                ByteBuffer columnName = column.getByteBuffer(comparator, 
variables);
                 validateColumnName(columnName);
                 rm.delete(new QueryPath(columnFamily, null, columnName), 
(timestamp == null) ? getTimestamp(clientState) : timestamp);
             }

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java Wed 
Dec 14 22:52:54 2011
@@ -73,7 +73,7 @@ public class QueryProcessor
 
     public static final String DEFAULT_KEY_NAME = 
bufferToString(CFMetaData.DEFAULT_KEY_NAME);
 
-    private static List<org.apache.cassandra.db.Row> getSlice(CFMetaData 
metadata, SelectStatement select)
+    private static List<org.apache.cassandra.db.Row> getSlice(CFMetaData 
metadata, SelectStatement select, List<String> variables)
     throws InvalidRequestException, TimedOutException, UnavailableException
     {
         QueryPath queryPath = new QueryPath(select.getColumnFamily());
@@ -82,12 +82,12 @@ public class QueryProcessor
         // ...of a list of column names
         if (!select.isColumnRange())
         {
-            Collection<ByteBuffer> columnNames = getColumnNames(select, 
metadata);
+            Collection<ByteBuffer> columnNames = getColumnNames(select, 
metadata, variables);
             validateColumnNames(columnNames);
 
             for (Term rawKey: select.getKeys())
             {
-                ByteBuffer key = 
rawKey.getByteBuffer(metadata.getKeyValidator());
+                ByteBuffer key = 
rawKey.getByteBuffer(metadata.getKeyValidator(),variables);
 
                 validateKey(key);
                 commands.add(new SliceByNamesReadCommand(metadata.ksName, key, 
queryPath, columnNames));
@@ -97,12 +97,12 @@ public class QueryProcessor
         else
         {
             AbstractType<?> comparator = select.getComparator(metadata.ksName);
-            ByteBuffer start = 
select.getColumnStart().getByteBuffer(comparator);
-            ByteBuffer finish = 
select.getColumnFinish().getByteBuffer(comparator);
+            ByteBuffer start = 
select.getColumnStart().getByteBuffer(comparator,variables);
+            ByteBuffer finish = 
select.getColumnFinish().getByteBuffer(comparator,variables);
 
             for (Term rawKey : select.getKeys())
             {
-                ByteBuffer key = 
rawKey.getByteBuffer(metadata.getKeyValidator());
+                ByteBuffer key = 
rawKey.getByteBuffer(metadata.getKeyValidator(),variables);
 
                 validateKey(key);
                 validateSliceRange(metadata, start, finish, 
select.isColumnsReversed());
@@ -130,7 +130,8 @@ public class QueryProcessor
         }
     }
 
-    private static List<ByteBuffer> getColumnNames(SelectStatement select, 
CFMetaData metadata) throws InvalidRequestException
+    private static List<ByteBuffer> getColumnNames(SelectStatement select, 
CFMetaData metadata, List<String> variables)
+    throws InvalidRequestException
     {
         String keyString = getKeyString(metadata);
         List<ByteBuffer> columnNames = new ArrayList<ByteBuffer>();
@@ -138,12 +139,12 @@ public class QueryProcessor
         {
             // skip the key for the slice op; we'll add it to the resultset in 
extractThriftColumns
             if (!column.getText().equalsIgnoreCase(keyString))
-                columnNames.add(column.getByteBuffer(metadata.comparator));
+                
columnNames.add(column.getByteBuffer(metadata.comparator,variables));
         }
         return columnNames;
     }
 
-    private static List<org.apache.cassandra.db.Row> 
multiRangeSlice(CFMetaData metadata, SelectStatement select)
+    private static List<org.apache.cassandra.db.Row> 
multiRangeSlice(CFMetaData metadata, SelectStatement select, List<String> 
variables)
     throws TimedOutException, UnavailableException, InvalidRequestException
     {
         List<org.apache.cassandra.db.Row> rows;
@@ -152,11 +153,11 @@ public class QueryProcessor
         AbstractType<?> keyType = 
Schema.instance.getCFMetaData(metadata.ksName, 
select.getColumnFamily()).getKeyValidator();
 
         ByteBuffer startKeyBytes = (select.getKeyStart() != null)
-                                   ? 
select.getKeyStart().getByteBuffer(keyType)
+                                   ? 
select.getKeyStart().getByteBuffer(keyType,variables)
                                    : null;
 
         ByteBuffer finishKeyBytes = (select.getKeyFinish() != null)
-                                    ? 
select.getKeyFinish().getByteBuffer(keyType)
+                                    ? 
select.getKeyFinish().getByteBuffer(keyType,variables)
                                     : null;
 
         RowPosition startKey = RowPosition.forKey(startKeyBytes, p), finishKey 
= RowPosition.forKey(finishKeyBytes, p);
@@ -170,7 +171,7 @@ public class QueryProcessor
         AbstractBounds<RowPosition> bounds = new Bounds<RowPosition>(startKey, 
finishKey);
         
         // XXX: Our use of Thrift structs internally makes me Sad. :(
-        SlicePredicate thriftSlicePredicate = slicePredicateFromSelect(select, 
metadata);
+        SlicePredicate thriftSlicePredicate = slicePredicateFromSelect(select, 
metadata,variables);
         validateSlicePredicate(metadata, thriftSlicePredicate);
 
         int limit = select.isKeyRange() && select.getKeyStart() != null
@@ -218,19 +219,19 @@ public class QueryProcessor
         return rows.subList(0, select.getNumRecords() < rows.size() ? 
select.getNumRecords() : rows.size());
     }
     
-    private static List<org.apache.cassandra.db.Row> 
getIndexedSlices(CFMetaData metadata, SelectStatement select)
+    private static List<org.apache.cassandra.db.Row> 
getIndexedSlices(CFMetaData metadata, SelectStatement select, List<String> 
variables)
     throws TimedOutException, UnavailableException, InvalidRequestException
     {
         // XXX: Our use of Thrift structs internally (still) makes me Sad. :~(
-        SlicePredicate thriftSlicePredicate = slicePredicateFromSelect(select, 
metadata);
+        SlicePredicate thriftSlicePredicate = slicePredicateFromSelect(select, 
metadata, variables);
         validateSlicePredicate(metadata, thriftSlicePredicate);
         
         List<IndexExpression> expressions = new ArrayList<IndexExpression>();
         for (Relation columnRelation : select.getColumnRelations())
         {
             // Left and right side of relational expression encoded according 
to comparator/validator.
-            ByteBuffer entity = 
columnRelation.getEntity().getByteBuffer(metadata.comparator);
-            ByteBuffer value = 
columnRelation.getValue().getByteBuffer(select.getValueValidator(metadata.ksName,
 entity));
+            ByteBuffer entity = 
columnRelation.getEntity().getByteBuffer(metadata.comparator, variables);
+            ByteBuffer value = 
columnRelation.getValue().getByteBuffer(select.getValueValidator(metadata.ksName,
 entity), variables);
             
             expressions.add(new IndexExpression(entity,
                                                 
IndexOperator.valueOf(columnRelation.operator().toString()),
@@ -238,7 +239,7 @@ public class QueryProcessor
         }
 
         AbstractType<?> keyType = 
Schema.instance.getCFMetaData(metadata.ksName, 
select.getColumnFamily()).getKeyValidator();
-        ByteBuffer startKey = (!select.isKeyRange()) ? (new 
Term()).getByteBuffer() : select.getKeyStart().getByteBuffer(keyType);
+        ByteBuffer startKey = (!select.isKeyRange()) ? (new 
Term()).getByteBuffer() : select.getKeyStart().getByteBuffer(keyType, 
variables);
         IndexClause thriftIndexClause = new IndexClause(expressions, startKey, 
select.getNumRecords());
         
         List<org.apache.cassandra.db.Row> rows;
@@ -262,7 +263,7 @@ public class QueryProcessor
         return rows;
     }
     
-    private static void batchUpdate(ClientState clientState, 
List<UpdateStatement> updateStatements, ConsistencyLevel consistency)
+    private static void batchUpdate(ClientState clientState, 
List<UpdateStatement> updateStatements, ConsistencyLevel consistency, 
List<String> variables )
     throws InvalidRequestException, UnavailableException, TimedOutException
     {
         String globalKeyspace = clientState.getKeyspace();
@@ -280,7 +281,7 @@ public class QueryProcessor
                 cfamsSeen.add(update.getColumnFamily());
             }
 
-            rowMutations.addAll(update.prepareRowMutations(keyspace, 
clientState));
+            rowMutations.addAll(update.prepareRowMutations(keyspace, 
clientState, variables));
         }
         
         try
@@ -297,7 +298,7 @@ public class QueryProcessor
         }
     }
     
-    private static SlicePredicate slicePredicateFromSelect(SelectStatement 
select, CFMetaData metadata)
+    private static SlicePredicate slicePredicateFromSelect(SelectStatement 
select, CFMetaData metadata, List<String> variables)
     throws InvalidRequestException
     {
         SlicePredicate thriftSlicePredicate = new SlicePredicate();
@@ -305,22 +306,22 @@ public class QueryProcessor
         if (select.isColumnRange() || select.getColumnNames().size() == 0)
         {
             SliceRange sliceRange = new SliceRange();
-            sliceRange.start = 
select.getColumnStart().getByteBuffer(metadata.comparator);
-            sliceRange.finish = 
select.getColumnFinish().getByteBuffer(metadata.comparator);
+            sliceRange.start = 
select.getColumnStart().getByteBuffer(metadata.comparator, variables);
+            sliceRange.finish = 
select.getColumnFinish().getByteBuffer(metadata.comparator, variables);
             sliceRange.reversed = select.isColumnsReversed();
             sliceRange.count = select.getColumnsLimit();
             thriftSlicePredicate.slice_range = sliceRange;
         }
         else
         {
-            thriftSlicePredicate.column_names = getColumnNames(select, 
metadata);
+            thriftSlicePredicate.column_names = getColumnNames(select, 
metadata, variables);
         }
         
         return thriftSlicePredicate;
     }
     
     /* Test for SELECT-specific taboos */
-    private static void validateSelect(String keyspace, SelectStatement 
select) throws InvalidRequestException
+    private static void validateSelect(String keyspace, SelectStatement 
select, List<String> variables) throws InvalidRequestException
     {
         ThriftValidation.validateConsistencyLevel(keyspace, 
select.getConsistencyLevel(), RequestType.READ);
 
@@ -346,7 +347,7 @@ public class QueryProcessor
             Set<ByteBuffer> indexed = 
Table.open(keyspace).getColumnFamilyStore(select.getColumnFamily()).indexManager.getIndexedColumns();
             for (Relation relation : select.getColumnRelations())
             {
-                if ((relation.operator() == RelationType.EQ) && 
indexed.contains(relation.getEntity().getByteBuffer(comparator)))
+                if ((relation.operator() == RelationType.EQ) && 
indexed.contains(relation.getEntity().getByteBuffer(comparator, variables)))
                     return;
             }
             throw new InvalidRequestException("No indexed columns present in 
by-columns clause with \"equals\" operator");
@@ -493,12 +494,125 @@ public class QueryProcessor
                                
Predicates.not(Predicates.equalTo(StorageProxy.UNREACHABLE)));
     }
 
-    public static CqlResult process(String queryString, ClientState 
clientState)
-    throws RecognitionException, UnavailableException, 
InvalidRequestException, TimedOutException, SchemaDisagreementException
+    
+    private final static void maybeAddBoundTerm(CQLStatement statement, Term 
term) throws InvalidRequestException
+    {
+        if (term != null && term.isBindMarker())
+        {
+            term.setBindIndex(statement.boundTerms++);
+        }
+    }
+    
+    public static void discoverBoundTerms(CQLStatement statement) throws 
InvalidRequestException
+    {
+        switch (statement.type)
+        {
+            case SELECT:
+                SelectStatement select = (SelectStatement)statement.statement;
+                if (logger.isTraceEnabled()) logger.trace(select.toString());
+                
+                // handle the select expression first
+                if (!select.isColumnRange() )
+                {
+                    List<Term> list = select.getColumnNames();
+                    for (Term term : list) maybeAddBoundTerm(statement,term);
+                }
+                else
+                {
+                    maybeAddBoundTerm(statement,select.getColumnStart());
+                    maybeAddBoundTerm(statement,select.getColumnFinish());
+                }
+                
+                // next handle the WHERE clause NB order is VERY important
+                
+                // first check for a multi-key (IN) list
+                
+                if (select.isMultiKey())
+                {
+                    for (Term term : select.getKeys()) 
maybeAddBoundTerm(statement,term);
+                }
+                else if (!select.getColumnRelations().isEmpty())
+                {
+                    if (select.isKeyRange())
+                    {
+                        maybeAddBoundTerm(statement,select.getKeyStart());
+                        maybeAddBoundTerm(statement,select.getKeyFinish());
+                    }
+
+                    for (Relation relation : select.getColumnRelations())
+                    {
+                        maybeAddBoundTerm(statement,relation.getEntity());
+                        maybeAddBoundTerm(statement,relation.getValue());
+                    }                    
+                }
+                else
+                {
+                    // maybe its empty or just a simple term
+                    for (Term term : select.getKeys()) 
maybeAddBoundTerm(statement,term);                   
+                }
+
+               break;
+                
+            case UPDATE:
+                UpdateStatement update = (UpdateStatement)statement.statement;
+                if (logger.isTraceEnabled()) logger.trace(update.toString());
+               
+                // first handle the SET clause values that come in pairs for 
UPDATE. NB the order of the markers (?)
+                for (Map.Entry<Term, Operation> column : 
update.getColumns().entrySet())
+                {
+                    maybeAddBoundTerm(statement,column.getKey());
+                    maybeAddBoundTerm(statement,column.getValue().a);
+                }
+                
+                // now handle the key(s) in the WHERE clause
+
+                for (Term term : update.getKeys()) 
maybeAddBoundTerm(statement,term);
+                break;
+                
+            case INSERT: // insert uses UpdateStatement but with different 
marker ordering
+                UpdateStatement insert = (UpdateStatement)statement.statement;
+                if (logger.isTraceEnabled()) logger.trace(insert.toString());
+                                
+                // first handle the INTO..VALUES clause values that are 
grouped in order for INSERT. NB the order of the markers (?)
+                for (Term term : insert.getColumnNames()) 
maybeAddBoundTerm(statement,term);
+                for (Term term : insert.getColumnValues()) 
maybeAddBoundTerm(statement,term);
+
+                // now handle the key(s) in the VALUES clause 
+                for (Term term : insert.getKeys()) 
maybeAddBoundTerm(statement,term);
+                break;
+
+            case DELETE:
+                DeleteStatement delete = (DeleteStatement)statement.statement;
+                if (logger.isTraceEnabled()) logger.trace(delete.toString());
+
+                // first handle the columns list for DELETE. NB the order of 
the markers (?)
+                for (Term term : delete.getColumns()) 
maybeAddBoundTerm(statement,term);
+
+                // now handle the key(s) in the WHERE clause 
+                for (Term term : delete.getKeys()) 
maybeAddBoundTerm(statement,term);
+                break;
+
+            case CREATE_COLUMNFAMILY:
+                CreateColumnFamilyStatement createCf = 
(CreateColumnFamilyStatement)statement.statement;
+                
+                // handle the left hand Terms. Not terribly useful but 
included for completeness
+                for (Term term : createCf.getColumns().keySet()) 
maybeAddBoundTerm(statement,term);
+                break;
+
+            case CREATE_INDEX:
+                CreateIndexStatement createIdx = 
(CreateIndexStatement)statement.statement;
+                
+                // handle the column name Term. Not terribly useful but 
included for completeness
+                maybeAddBoundTerm(statement,createIdx.getColumnName());
+                break;
+                
+           default: // all other statement types are a NOOP.                   
 
+        }
+    }
+    
+    public static CqlResult doTheStatement(CQLStatement statement,ClientState 
clientState, List<String> variables )
+    throws  UnavailableException, InvalidRequestException, TimedOutException, 
SchemaDisagreementException
     {
-        logger.trace("CQL QUERY: {}", queryString);
-        
-        CQLStatement statement = getStatement(queryString);
         String keyspace = null;
         
         // Some statements won't have (or don't need) a keyspace (think USE, 
or CREATE).
@@ -507,7 +621,7 @@ public class QueryProcessor
 
         CqlResult result = new CqlResult();
         
-        logger.debug("CQL statement type: {}", statement.type.toString());
+        if (logger.isDebugEnabled()) logger.debug("CQL statement type: {}", 
statement.type.toString());
         CFMetaData metadata;
         switch (statement.type)
         {
@@ -535,26 +649,26 @@ public class QueryProcessor
                 if (select.getKeys().size() > 0)
                     validateKeyAlias(metadata, select.getKeyAlias());
 
-                validateSelect(keyspace, select);
+                validateSelect(keyspace, select, variables);
 
                 List<org.apache.cassandra.db.Row> rows;
 
                 // By-key
                 if (!select.isKeyRange() && (select.getKeys().size() > 0))
                 {
-                    rows = getSlice(metadata, select);
+                    rows = getSlice(metadata, select, variables);
                 }
                 else
                 {
                     // Range query
                     if ((select.getKeyFinish() != null) || 
(select.getColumnRelations().size() == 0))
                     {
-                        rows = multiRangeSlice(metadata, select);
+                        rows = multiRangeSlice(metadata, select, variables);
                     }
                     // Index scan
                     else
                     {
-                        rows = getIndexedSlices(metadata, select);
+                        rows = getIndexedSlices(metadata, select, variables);
                     }
                 }
 
@@ -632,7 +746,7 @@ public class QueryProcessor
                             ByteBuffer name;
                             try
                             {
-                                name = term.getByteBuffer(metadata.comparator);
+                                name = term.getByteBuffer(metadata.comparator, 
variables);
                             }
                             catch (InvalidRequestException e)
                             {
@@ -666,7 +780,7 @@ public class QueryProcessor
             case UPDATE:
                 UpdateStatement update = (UpdateStatement)statement.statement;
                 ThriftValidation.validateConsistencyLevel(keyspace, 
update.getConsistencyLevel(), RequestType.WRITE);
-                batchUpdate(clientState, Collections.singletonList(update), 
update.getConsistencyLevel());
+                batchUpdate(clientState, Collections.singletonList(update), 
update.getConsistencyLevel(),variables);
                 result.type = CqlResultType.VOID;
                 return result;
                 
@@ -690,7 +804,7 @@ public class QueryProcessor
 
                 try
                 {
-                    StorageProxy.mutate(batch.getMutations(keyspace, 
clientState), batch.getConsistencyLevel());
+                    StorageProxy.mutate(batch.getMutations(keyspace, 
clientState, variables), batch.getConsistencyLevel());
                 }
                 catch (org.apache.cassandra.thrift.UnavailableException e)
                 {
@@ -740,7 +854,7 @@ public class QueryProcessor
 
                 try
                 {
-                    StorageProxy.mutate(delete.prepareRowMutations(keyspace, 
clientState), delete.getConsistencyLevel());
+                    StorageProxy.mutate(delete.prepareRowMutations(keyspace, 
clientState, variables), delete.getConsistencyLevel());
                 }
                 catch (TimeoutException e)
                 {
@@ -786,7 +900,7 @@ public class QueryProcessor
                 CreateColumnFamilyStatement createCf = 
(CreateColumnFamilyStatement)statement.statement;
                 clientState.hasColumnFamilySchemaAccess(Permission.WRITE);
                 validateSchemaAgreement();
-                CFMetaData cfmd = createCf.getCFMetaData(keyspace);
+                CFMetaData cfmd = createCf.getCFMetaData(keyspace,variables);
                 ThriftValidation.validateCfDef(cfmd.toThrift(), null);
 
                 try
@@ -828,7 +942,8 @@ public class QueryProcessor
                     {
                         if (cd.index_type != null)
                             throw new InvalidRequestException("Index already 
exists");
-                        logger.debug("Updating column {} definition for index 
{}", oldCfm.comparator.getString(columnName), createIdx.getIndexName());
+                        if (logger.isDebugEnabled()) 
+                            logger.debug("Updating column {} definition for 
index {}", oldCfm.comparator.getString(columnName), createIdx.getIndexName());
                         cd.setIndex_type(IndexType.KEYS);
                         cd.setIndex_name(createIdx.getIndexName());
                         columnExists = true;
@@ -971,10 +1086,53 @@ public class QueryProcessor
                 result.type = CqlResultType.VOID;
                 return result;
         }
-        
         return null;    // We should never get here.
     }
 
+    public static CqlResult process(String queryString, ClientState 
clientState)
+    throws RecognitionException, UnavailableException, 
InvalidRequestException, TimedOutException, SchemaDisagreementException
+    {
+        if (logger.isDebugEnabled()) logger.debug("CQL QUERY: {}", 
queryString);
+        
+        CQLStatement statement = getStatement(queryString);
+        
+        CqlResult result = doTheStatement(statement, clientState, new 
ArrayList<String>());
+        
+        return result;
+    }
+
+    public static CQLStatement prepare (String queryString, ClientState 
clientState)
+                    throws RecognitionException, InvalidRequestException
+    {
+        if (logger.isDebugEnabled()) logger.debug("CQL QUERY: {}", 
queryString);
+
+        CQLStatement statement = getStatement(queryString);
+
+        return statement;
+    }
+   
+    public static CqlResult process_prepared(CQLStatement statement, 
ClientState clientState, List<String> variables)
+    throws UnavailableException, InvalidRequestException, TimedOutException, 
SchemaDisagreementException
+    {
+        // Check to see if there are any bound variables to verify 
+        if (!(variables.isEmpty() && (statement.boundTerms==0)))
+        {
+            if (variables.size() != statement.boundTerms) 
+                throw new InvalidRequestException(String.format("there were %d 
markers(?) in CQL but  %d bound variables",
+                statement.boundTerms, variables.size()));
+
+            // at this point there is a match in count between markers and 
variables that is non-zero
+
+            if (logger.isTraceEnabled()) 
+                for (int i = 0; i < variables.size(); i++) logger.trace("[{}] 
'{}'",i+1,variables.get(i));
+        }
+
+        CqlResult result = doTheStatement(statement, clientState, variables);
+
+        return result;
+    }
+    
+
     private static Column thriftify(IColumn c)
     {
         ByteBuffer value = (c instanceof CounterColumn)

Modified: 
cassandra/trunk/src/java/org/apache/cassandra/cql/SelectExpression.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/SelectExpression.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/SelectExpression.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/SelectExpression.java Wed 
Dec 14 22:52:54 2011
@@ -147,4 +147,18 @@ public class SelectExpression
     {
         return wildcard;
     }
+
+    public String toString()
+    {
+        return String.format("SelectExpression [numColumns=%s, 
reverseColumns=%s, hasFirstSet=%s, wildcard=%s, start=%s, finish=%s, 
columns=%s]",
+            numColumns,
+            reverseColumns,
+            hasFirstSet,
+            wildcard,
+            start,
+            finish,
+            columns);
+    }
+    
+    
 }

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/SelectStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/SelectStatement.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/SelectStatement.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/SelectStatement.java Wed 
Dec 14 22:52:54 2011
@@ -185,4 +185,17 @@ public class SelectStatement
         return Schema.instance.getValueValidator(keyspace, columnFamily, 
column);
     }
 
+    public String toString()
+    {
+        return String.format("SelectStatement [expression=%s, isCountOper=%s, 
columnFamily=%s, keyspace=%s, cLevel=%s, clause=%s, numRecords=%s]",
+            expression,
+            isCountOper,
+            columnFamily,
+            keyspace,
+            cLevel,
+            clause,
+            numRecords);
+    }
+    
+
 }

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/Term.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/Term.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/Term.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/Term.java Wed Dec 14 
22:52:54 2011
@@ -21,6 +21,7 @@
 package org.apache.cassandra.cql;
 
 import java.nio.ByteBuffer;
+import java.util.List;
 
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.db.marshal.AsciiType;
@@ -36,6 +37,8 @@ public class Term
     private final String text;
     private final TermType type;
     
+    private Integer bindIndex;
+    
     /**
      * Create new Term instance from a string, and an integer that corresponds
      * with the token ID from CQLParser.
@@ -66,6 +69,11 @@ public class Term
         this.text = "";
         this.type = TermType.STRING;
     }
+    
+    public void setBindIndex(int bindIndex)
+    {
+        this.bindIndex = bindIndex;
+    }
 
     /**
      * Returns the text parsed to create this term.
@@ -76,7 +84,7 @@ public class Term
     {
         return text;
     }
-    
+        
     /**
      * Returns the typed value, serialized to a ByteBuffer according to a
      * comparator/validator.
@@ -84,11 +92,18 @@ public class Term
      * @return a ByteBuffer of the value.
      * @throws InvalidRequestException if unable to coerce the string to its 
type.
      */
-    public ByteBuffer getByteBuffer(AbstractType<?> validator) throws 
InvalidRequestException
+    public ByteBuffer getByteBuffer(AbstractType<?> validator, List<String> 
variables) throws InvalidRequestException
     {
         try
         {
-            return validator.fromString(text);
+            if (!isBindMarker()) return validator.fromString(text);
+            
+            // must be a marker term so check for a CqlBindValue stored in the 
term
+            if (bindIndex==null) throw new AssertionError("a marker Term was 
encountered with no index value");
+            
+            String bindValue = variables.get(bindIndex);
+                        
+            return validator.fromString(bindValue);
         }
         catch (MarshalException e)
         {
@@ -136,6 +151,11 @@ public class Term
     {
         return String.format("Term(%s, type=%s)", getText(), type);
     }
+    
+    public boolean isBindMarker()
+    {
+        return type==TermType.QMARK;
+    }
 
     @Override
     public int hashCode()
@@ -157,6 +177,7 @@ public class Term
         if (getClass() != obj.getClass())
             return false;
         Term other = (Term) obj;
+        if (type==TermType.QMARK) return false; // markers are never equal 
         if (text == null)
         {
             if (other.text != null)
@@ -173,7 +194,7 @@ public class Term
 
 enum TermType
 {
-    STRING, INTEGER, UUID, FLOAT;
+    STRING, INTEGER, UUID, FLOAT, QMARK;
     
     static TermType forInt(int type)
     {
@@ -184,7 +205,9 @@ enum TermType
         else if (type == CqlParser.UUID)
           return UUID;
         else if (type == CqlParser.FLOAT)
-          return FLOAT;
+            return FLOAT;
+        else if (type == CqlParser.QMARK)
+            return QMARK;
         
         // FIXME: handled scenario that should never occur.
         return null;

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/UpdateStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/UpdateStatement.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/UpdateStatement.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/UpdateStatement.java Wed 
Dec 14 22:52:54 2011
@@ -124,13 +124,13 @@ public class UpdateStatement extends Abs
     }
 
     /** {@inheritDoc} */
-    public List<IMutation> prepareRowMutations(String keyspace, ClientState 
clientState) throws InvalidRequestException
+    public List<IMutation> prepareRowMutations(String keyspace, ClientState 
clientState, List<String> variables) throws InvalidRequestException
     {
-        return prepareRowMutations(keyspace, clientState, null);
+        return prepareRowMutations(keyspace, clientState, null, variables);
     }
 
     /** {@inheritDoc} */
-    public List<IMutation> prepareRowMutations(String keyspace, ClientState 
clientState, Long timestamp) throws InvalidRequestException
+    public List<IMutation> prepareRowMutations(String keyspace, ClientState 
clientState, Long timestamp, List<String> variables) throws 
InvalidRequestException
     {
         List<String> cfamsSeen = new ArrayList<String>();
 
@@ -162,7 +162,7 @@ public class UpdateStatement extends Abs
 
         for (Term key: keys)
         {
-            rowMutations.add(mutationForKey(keyspace, 
key.getByteBuffer(getKeyType(keyspace)), metadata, timestamp, clientState));
+            rowMutations.add(mutationForKey(keyspace, 
key.getByteBuffer(getKeyType(keyspace),variables), metadata, timestamp, 
clientState, variables));
         }
 
         return rowMutations;
@@ -182,7 +182,8 @@ public class UpdateStatement extends Abs
      *
      * @throws InvalidRequestException on the wrong request
      */
-    private IMutation mutationForKey(String keyspace, ByteBuffer key, 
CFMetaData metadata, Long timestamp, ClientState clientState) throws 
InvalidRequestException
+    private IMutation mutationForKey(String keyspace, ByteBuffer key, 
CFMetaData metadata, Long timestamp, ClientState clientState, List<String> 
variables)
+    throws InvalidRequestException
     {
         AbstractType<?> comparator = getComparator(keyspace);
 
@@ -192,7 +193,7 @@ public class UpdateStatement extends Abs
 
         for (Map.Entry<Term, Operation> column : getColumns().entrySet())
         {
-            ByteBuffer colName = column.getKey().getByteBuffer(comparator);
+            ByteBuffer colName = column.getKey().getByteBuffer(comparator, 
variables);
             Operation op = column.getValue();
 
             if (op.isUnary())
@@ -200,7 +201,7 @@ public class UpdateStatement extends Abs
                 if (hasCounterColumn)
                     throw new InvalidRequestException("Mix of commutative and 
non-commutative operations is not allowed.");
 
-                ByteBuffer colValue = 
op.a.getByteBuffer(getValueValidator(keyspace, colName));
+                ByteBuffer colValue = 
op.a.getByteBuffer(getValueValidator(keyspace, colName),variables);
 
                 validateColumn(metadata, colName, colValue);
                 rm.add(new QueryPath(columnFamily, null, colName),
@@ -239,7 +240,6 @@ public class UpdateStatement extends Abs
         return columnFamily;
     }
 
-    /** {@inheritDoc} */
     public List<Term> getKeys()
     {
         return keys;
@@ -293,4 +293,15 @@ public class UpdateStatement extends Abs
     {
         return Schema.instance.getValueValidator(keyspace, columnFamily, 
column);
     }
+
+    public List<Term> getColumnNames()
+    {
+        return columnNames;
+    }
+
+    public List<Term> getColumnValues()
+    {
+        return columnValues;
+    }
+    
 }

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/WhereClause.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/WhereClause.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/WhereClause.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/WhereClause.java Wed Dec 
14 22:52:54 2011
@@ -182,4 +182,19 @@ public class WhereClause
             }
         }
     }
+
+    public String toString()
+    {
+        return String.format("WhereClause [keys=%s, startKey=%s, finishKey=%s, 
columns=%s, includeStartKey=%s, includeFinishKey=%s, multiKey=%s, keyAlias=%s]",
+            keys,
+            startKey,
+            finishKey,
+            columns,
+            includeStartKey,
+            includeFinishKey,
+            multiKey,
+            keyAlias);
+    }
+    
+
 }

Modified: cassandra/trunk/src/java/org/apache/cassandra/service/ClientState.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/service/ClientState.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/service/ClientState.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/service/ClientState.java Wed 
Dec 14 22:52:54 2011
@@ -19,6 +19,7 @@
 package org.apache.cassandra.service;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -30,6 +31,7 @@ import org.apache.cassandra.auth.Authent
 import org.apache.cassandra.auth.Permission;
 import org.apache.cassandra.auth.Resources;
 import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.cql.CQLStatement;
 import org.apache.cassandra.db.Table;
 import org.apache.cassandra.thrift.AuthenticationException;
 import org.apache.cassandra.thrift.InvalidRequestException;
@@ -48,6 +50,9 @@ public class ClientState
     // Reusable array for authorization
     private final List<Object> resource = new ArrayList<Object>();
 
+    // a map of prepared statements index by an integer
+    private Map<Integer,CQLStatement> prepared = new 
HashMap<Integer,CQLStatement>();
+
     private long clock;
 
     /**
@@ -58,6 +63,11 @@ public class ClientState
         reset();
     }
 
+    public Map<Integer, CQLStatement> getPrepared()
+    {
+        return prepared;
+    }
+    
     public String getRawKeyspace()
     {
         return keyspace;
@@ -114,6 +124,7 @@ public class ClientState
         user = DatabaseDescriptor.getAuthenticator().defaultUser();
         keyspace = null;
         resourceClear();
+        prepared.clear();
     }
 
     /**

Modified: 
cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java
URL: 
http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java?rev=1214520&r1=1214519&r2=1214520&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java 
(original)
+++ cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java 
Wed Dec 14 22:52:54 2011
@@ -41,6 +41,7 @@ import org.apache.cassandra.auth.Permiss
 import org.apache.cassandra.concurrent.Stage;
 import org.apache.cassandra.concurrent.StageManager;
 import org.apache.cassandra.config.*;
+import org.apache.cassandra.cql.CQLStatement;
 import org.apache.cassandra.cql.QueryProcessor;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.filter.QueryPath;
@@ -985,7 +986,8 @@ public class CassandraServer implements 
     }
 
     /** update an existing keyspace, but do not allow column family 
modifications. 
-     * @throws SchemaDisagreementException */
+     * @throws SchemaDisagreementException
+     */
     public synchronized String system_update_keyspace(KsDef ks_def)
     throws InvalidRequestException, SchemaDisagreementException, TException
     {
@@ -1149,9 +1151,8 @@ public class CassandraServer implements 
 
         internal_remove(key, path, System.currentTimeMillis(), 
consistency_level, true);
     }
-
-    public CqlResult execute_cql_query(ByteBuffer query, Compression 
compression)
-    throws InvalidRequestException, UnavailableException, TimedOutException, 
SchemaDisagreementException, TException
+    
+    private static String uncompress(ByteBuffer query, Compression 
compression) throws InvalidRequestException
     {
         String queryString = null;
         
@@ -1161,7 +1162,7 @@ public class CassandraServer implements 
             switch (compression)
             {
                 case GZIP:
-                       FastByteArrayOutputStream byteArray = new 
FastByteArrayOutputStream();
+                    FastByteArrayOutputStream byteArray = new 
FastByteArrayOutputStream();
                     byte[] outBuffer = new byte[1024], inBuffer = new 
byte[1024];
                     
                     Inflater decompressor = new Inflater();
@@ -1206,7 +1207,16 @@ public class CassandraServer implements 
         {
             throw new InvalidRequestException("Unknown query string 
encoding.");
         }
+        return queryString;
+    }
+
+    public CqlResult execute_cql_query(ByteBuffer query, Compression 
compression)
+    throws InvalidRequestException, UnavailableException, TimedOutException, 
SchemaDisagreementException, TException
+    {
+        if (logger.isDebugEnabled()) logger.debug("execute_cql_query");
         
+        String queryString = uncompress(query,compression);
+                
         try
         {
             return QueryProcessor.process(queryString, state());
@@ -1218,6 +1228,54 @@ public class CassandraServer implements 
             throw ire;
         }
     }
+   
+    private static final int makeItemId(String cql)
+    {
+       // use the hash of the string till something better is provided
+        return cql.hashCode();
+    }
+    
+    public CqlPreparedResult prepare_cql_query(ByteBuffer query, Compression 
compression)
+    throws InvalidRequestException, TException
+    {
+        if (logger.isDebugEnabled()) logger.debug("prepare_cql_query");
+                
+        String queryString = uncompress(query,compression);
+        int itemId = makeItemId(queryString);
+        
+        try
+        {
+            CQLStatement statement = QueryProcessor.prepare(queryString, 
state());
+            
+            // discover all the marked Terms and hang them off of statement 
for use later
+            QueryProcessor.discoverBoundTerms(statement);
+            if (logger.isTraceEnabled()) logger.trace("Discovered "+ 
statement.boundTerms + " bound variables.");
+            
+            // put the prepared Statement into the Map
+            state().getPrepared().put(itemId, statement);
+            if (logger.isTraceEnabled()) logger.trace("Storing prepared 
statement: #"+ itemId + " count:"+state().getPrepared().size());
+            return new CqlPreparedResult(itemId, statement.boundTerms);
+        }
+        catch (RecognitionException e)
+        {
+            InvalidRequestException ire = new InvalidRequestException("Invalid 
or malformed CQL query string");
+            ire.initCause(e);
+            throw ire;
+        }
+    }
+    
+    
+   public CqlResult execute_prepared_cql_query(int itemId,List<String> 
bindVariables)
+    throws InvalidRequestException, UnavailableException, TimedOutException, 
SchemaDisagreementException, TException
+    {
+        if (logger.isDebugEnabled()) 
logger.debug("execute_prepared_cql_query");
+        
+        CQLStatement statement = state().getPrepared().get(itemId);
+        if (logger.isTraceEnabled()) logger.trace("Retreving prepared 
statement: #"+ itemId + " count:"+state().getPrepared().size());
+        
+        CqlResult result = QueryProcessor.process_prepared(statement, state(), 
bindVariables);
+        return result;
+    }
 
     // main method moved to CassandraDaemon
 }


Reply via email to