Author: jbellis
Date: Sat Jun 11 01:45:16 2011
New Revision: 1134502

URL: http://svn.apache.org/viewvc?rev=1134502&view=rev
Log:
add cql key alias support
patch by pyaskevich; reviewed by jbellis for CASSANDRA-2480

Modified:
    cassandra/branches/cassandra-0.8/CHANGES.txt
    cassandra/branches/cassandra-0.8/doc/cql/CQL.textile
    
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/config/CFMetaData.java
    
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AbstractModification.java
    cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g
    
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
    
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/DeleteStatement.java
    
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
    
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java
    
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/UpdateStatement.java
    
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java
    cassandra/branches/cassandra-0.8/test/system/test_cql.py

Modified: cassandra/branches/cassandra-0.8/CHANGES.txt
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/CHANGES.txt?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/CHANGES.txt (original)
+++ cassandra/branches/cassandra-0.8/CHANGES.txt Sat Jun 11 01:45:16 2011
@@ -10,6 +10,7 @@
    - DROP INDEX (CASSANDRA-2617)
    - add SCHEMA/TABLE as aliases for KS/CF (CASSANDRA-2743)
    - server handles wait-for-schema-agreement (CASSANDRA-2756)
+   - key alias support (CASSANDRA-2480)
  * add support for comparator parameters and a generic ReverseType
    (CASSANDRA-2355)
  * add CompositeType and DynamicCompositeType (CASSANDRA-2231)

Modified: cassandra/branches/cassandra-0.8/doc/cql/CQL.textile
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/doc/cql/CQL.textile?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/doc/cql/CQL.textile (original)
+++ cassandra/branches/cassandra-0.8/doc/cql/CQL.textile Sat Jun 11 01:45:16 
2011
@@ -54,11 +54,11 @@ Following the column family clause is an
 h3. Filtering rows
 
 bc. 
-SELECT ... WHERE KEY = keyname AND name1 = value1
-SELECT ... WHERE KEY >= startkey and KEY =< endkey AND name1 = value1
-SELECT ... WHERE KEY IN ('<key>', '<key>', '<key>', ...)
+SELECT ... WHERE <KEY> = keyname AND name1 = value1
+SELECT ... WHERE <KEY> >= startkey and <KEY> =< endkey AND name1 = value1
+SELECT ... WHERE <KEY> IN ('<key>', '<key>', '<key>', ...)
 
-The WHERE clause provides for filtering the rows that appear in results.  The 
clause can filter on a key name, or range of keys, and in the case of indexed 
columns, on column values.  Key filters are specified using the @KEY@ keyword, 
a relational operator, (one of @=@, @>@, @>=@, @<@, and @<=@), and a term 
value.  When terms appear on both sides of a relational operator it is assumed 
the filter applies to an indexed column. With column index filters, the term on 
the left of the operator is the name, the term on the right is the value to 
filter __on__.
+The WHERE clause provides for filtering the rows that appear in results.  The 
clause can filter on a key name, or range of keys, and in the case of indexed 
columns, on column values.  Key filters are specified using the @KEY@ keyword 
or key alias name, a relational operator, (one of @=@, @>@, @>=@, @<@, and 
@<=@), and a term value.  When terms appear on both sides of a relational 
operator it is assumed the filter applies to an indexed column. With column 
index filters, the term on the left of the operator is the name, the term on 
the right is the value to filter __on__.
 
 __Note: The greater-than and less-than operators (@>@ and @<@) result in key 
ranges that are inclusive of the terms. There is no supported notion of 
"strictly" greater-than or less-than; these operators are merely supported as 
aliases to @>=@ and @<=@.__  
 
@@ -85,7 +85,7 @@ h2. INSERT
 _Synopsis:_
 
 bc.
-INSERT INTO <COLUMN FAMILY> (KEY, <col>, <col>, ...) VALUES (<key>, <val>, 
<val>, ...) [USING CONSISTENCY <LEVEL> [AND TIMESTAMP <timestamp>] [AND TTL 
<timeToLive>]];
+INSERT INTO <COLUMN FAMILY> (<KEY>, <col>, <col>, ...) VALUES (<key>, <val>, 
<val>, ...) [USING CONSISTENCY <LEVEL> [AND TIMESTAMP <timestamp>] [AND TTL 
<timeToLive>]];
 
 An @INSERT@ is used to write one or more columns to a record in a Cassandra 
column family. No results are returned. 
 
@@ -97,9 +97,9 @@ _Synopsis:_
 
 bc. 
 UPDATE <COLUMN FAMILY> [USING <CONSISTENCY> [AND TIMESTAMP <timestamp>] [AND 
TTL <timeToLive>]]
-        SET name1 = value1, name2 = value2 WHERE KEY = keyname;
+        SET name1 = value1, name2 = value2 WHERE <KEY> = keyname;
         
-An @UPDATE@ is used to write one or more columns to a record in a Cassandra 
column family. No results are returned.
+An @UPDATE@ is used to write one or more columns to a record in a Cassandra 
column family. No results are returned. Key can be given using @KEY@ keyword or 
by alias set per ColumnFamily.
 
 h3. Column Family
 
@@ -132,20 +132,20 @@ UPDATE ... [USING TTL <timeToLive>] ...
 h3. Specifying Columns and Row
 
 bc. 
-UPDATE ... SET name1 = value1, name2 = value2 WHERE KEY = keyname;
-UPDATE ... SET name1 = value1, name2 = value2 WHERE KEY IN ('<key>', '<key>', 
...)
+UPDATE ... SET name1 = value1, name2 = value2 WHERE <KEY> = keyname;
+UPDATE ... SET name1 = value1, name2 = value2 WHERE <KEY> IN ('<key>', 
'<key>', ...)
 
-Rows are created or updated by supplying column names and values in term 
assignment format. Multiple columns can be set by separating the name/value 
pairs using commas.  Each update statement requires exactly one key to be 
specified using a WHERE clause and the @KEY@ keyword.
+Rows are created or updated by supplying column names and values in term 
assignment format. Multiple columns can be set by separating the name/value 
pairs using commas.  Each update statement requires exactly one key to be 
specified using a WHERE clause and the @KEY@ keyword or key alias.
 
 h2. DELETE
 
 _Synopsis:_
 
 bc. 
-DELETE [COLUMNS] FROM <COLUMN FAMILY> [USING <CONSISTENCY>] WHERE KEY = 
keyname1
-DELETE [COLUMNS] FROM <COLUMN FAMILY> [USING <CONSISTENCY>] WHERE KEY IN 
(keyname1, keyname2);
+DELETE [COLUMNS] FROM <COLUMN FAMILY> [USING <CONSISTENCY>] WHERE <KEY> = 
keyname1
+DELETE [COLUMNS] FROM <COLUMN FAMILY> [USING <CONSISTENCY>] WHERE <KEY> IN 
(keyname1, keyname2);
 
-A @DELETE@ is used to perform the removal of one or more columns from one or 
more rows.
+A @DELETE@ is used to perform the removal of one or more columns from one or 
more rows. Key can be given using @KEY@ keyword or by alias set per 
ColumnFamily.
 
 h3. Specifying Columns
 
@@ -171,10 +171,10 @@ Following the column family identifier i
 h3(#deleterows). Specifying Rows
 
 bc. 
-DELETE ... WHERE KEY = keyname1
-DELETE ... WHERE KEY IN (keyname1, keyname2)
+DELETE ... WHERE <KEY> = keyname1
+DELETE ... WHERE <KEY> IN (keyname1, keyname2)
 
-The @WHERE@ clause is used to determine which row(s) a @DELETE@ applies to.  
The first form allows the specification of a single keyname using the @KEY@ 
keyword and the @=@ operator.  The second form allows a list of keyname terms 
to be specified using the @IN@ notation and a parenthesized list of 
comma-delimited keyname terms.
+The @WHERE@ clause is used to determine which row(s) a @DELETE@ applies to. 
The first form allows the specification of a single keyname using the @KEY@ 
keyword (or by key alias) and the @=@ operator.  The second form allows a list 
of keyname terms to be specified using the @IN@ notation and a parenthesized 
list of comma-delimited keyname terms.
 
 h2. BATCH
 
@@ -229,8 +229,8 @@ h2. CREATE COLUMNFAMILY
 _Synopsis:_
 
 bc. 
-CREATE COLUMNFAMILY <COLUMN FAMILY> (KEY <type> PRIMARY KEY [, name1 type, 
name2 type, ...]);
-CREATE COLUMNFAMILY <COLUMN FAMILY> (KEY <type> PRIMARY KEY [, name1 type, 
name2 type, ...])
+CREATE COLUMNFAMILY <COLUMN FAMILY> (<KEY> <type> PRIMARY KEY [, name1 type, 
name2 type, ...]);
+CREATE COLUMNFAMILY <COLUMN FAMILY> (<KEY> <type> PRIMARY KEY [, name1 type, 
name2 type, ...])
     [WITH keyword1 = arg1 [AND keyword2 = arg2 [AND ...]]];
 
 @CREATE COLUMNFAMILY@ statements create new column family namespaces under the 
current keyspace. Valid column family names are strings of alphanumeric 
characters and underscores, which begin with a letter.
@@ -238,14 +238,14 @@ CREATE COLUMNFAMILY <COLUMN FAMILY> (KEY
 h3(#keytypes). Specifying Key Type
 
 bc. 
-CREATE ... (KEY <type> PRIMARY KEY) ...
+CREATE ... (<KEY> <type> PRIMARY KEY) ...
 
-When creating a new column family, you must specify key type.  The list of 
possible key types is identical to column comparators/validators, (see 
"Specifying Column Type":columntypes).  It's important to note that the key 
type must be compatible with the partitioner in use, for example 
@OrderPreservingPartitioner@ and @CollatingOrderPreservingPartitioner@ both 
require UTF-8 keys.
+When creating a new column family, you must specify key type.  The list of 
possible key types is identical to column comparators/validators, (see 
"Specifying Column Type":columntypes).  It's important to note that the key 
type must be compatible with the partitioner in use, for example 
@OrderPreservingPartitioner@ and @CollatingOrderPreservingPartitioner@ both 
require UTF-8 keys. If you use name instead of @KEY@ keyword, name alias will 
be set automatically.
 
 h3(#columntypes). Specifying Column Type (optional)
 
 bc. 
-CREATE ... (KEY <type> PRIMARY KEY, name1 type, name2 type) ...
+CREATE ... (<KEY> <type> PRIMARY KEY, name1 type, name2 type) ...
 
 It is possible to assign columns a type during column family creation.  
Columns configured with a type are validated accordingly when a write occurs. 
Column types are specified as a parenthesized, comma-separated list of column 
term and type pairs.  The list of recognized types are:
 

Modified: 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/config/CFMetaData.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/config/CFMetaData.java?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/config/CFMetaData.java
 (original)
+++ 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/config/CFMetaData.java
 Sat Jun 11 01:45:16 2011
@@ -61,7 +61,8 @@ public final class CFMetaData
     public final static double DEFAULT_MEMTABLE_OPERATIONS_IN_MILLIONS = 
sizeMemtableOperations(DEFAULT_MEMTABLE_THROUGHPUT_IN_MB);
     public final static double DEFAULT_MERGE_SHARDS_CHANCE = 0.1;
     public final static String DEFAULT_ROW_CACHE_PROVIDER = 
"org.apache.cassandra.cache.ConcurrentLinkedHashCacheProvider";
-    
+    public final static ByteBuffer DEFAULT_KEY_NAME = 
ByteBufferUtil.bytes("KEY");
+
     private static final int MIN_CF_ID = 1000;
     private static final AtomicInteger idGen = new AtomicInteger(MIN_CF_ID);
     
@@ -73,7 +74,6 @@ public final class CFMetaData
     public static final CFMetaData SchemaCf = 
newSystemMetadata(Migration.SCHEMA_CF, 3, "current state of the schema", 
UTF8Type.instance, null, DEFAULT_SYSTEM_MEMTABLE_THROUGHPUT_IN_MB);
     public static final CFMetaData IndexCf = 
newSystemMetadata(SystemTable.INDEX_CF, 5, "indexes that have been completed", 
UTF8Type.instance, null, DEFAULT_SYSTEM_MEMTABLE_THROUGHPUT_IN_MB);
     public static final CFMetaData NodeIdCf = 
newSystemMetadata(SystemTable.NODE_ID_CF, 6, "nodeId and their metadata", 
TimeUUIDType.instance, null, DEFAULT_SYSTEM_MEMTABLE_THROUGHPUT_IN_MB);
-    private static final ByteBuffer DEFAULT_KEY_NAME = 
ByteBufferUtil.bytes("KEY");
 
     /**
      * @return A calculated memtable throughput size for this machine.

Modified: 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AbstractModification.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AbstractModification.java?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AbstractModification.java
 (original)
+++ 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/AbstractModification.java
 Sat Jun 11 01:45:16 2011
@@ -20,14 +20,13 @@
  */
 package org.apache.cassandra.cql;
 
+import java.util.List;
+
 import org.apache.cassandra.db.IMutation;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.thrift.ConsistencyLevel;
 import org.apache.cassandra.thrift.InvalidRequestException;
 
-import java.nio.ByteBuffer;
-import java.util.List;
-
 public abstract class AbstractModification
 {
     public static final ConsistencyLevel defaultConsistency = 
ConsistencyLevel.ONE;
@@ -36,18 +35,20 @@ public abstract class AbstractModificati
     protected final ConsistencyLevel cLevel;
     protected final Long timestamp;
     protected final int timeToLive;
+    protected final String keyName;
 
-    public AbstractModification(String columnFamily, Attributes attrs)
+    public AbstractModification(String columnFamily, String keyAlias, 
Attributes attrs)
     {
-        this(columnFamily, attrs.getConsistencyLevel(), attrs.getTimestamp(), 
attrs.getTimeToLive());
+        this(columnFamily, keyAlias, attrs.getConsistencyLevel(), 
attrs.getTimestamp(), attrs.getTimeToLive());
     }
 
-    public AbstractModification(String columnFamily, ConsistencyLevel cLevel, 
Long timestamp, int timeToLive)
+    public AbstractModification(String columnFamily, String keyAlias, 
ConsistencyLevel cLevel, Long timestamp, int timeToLive)
     {
         this.columnFamily = columnFamily;
         this.cLevel = cLevel;
         this.timestamp = timestamp;
         this.timeToLive = timeToLive;
+        this.keyName = keyAlias.toUpperCase();
     }
 
     public String getColumnFamily()

Modified: 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g 
(original)
+++ cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/Cql.g 
Sat Jun 11 01:45:16 2011
@@ -185,8 +185,9 @@ whereClause returns [WhereClause clause]
     }
     : first=relation { $clause = new WhereClause(first); } 
           (K_AND next=relation { $clause.and(next); })*
-      | K_KEY K_IN '(' f1=term { inClause.andKeyEquals(f1); }
-                      (',' fN=term { inClause.andKeyEquals(fN); } )* ')'
+      | key_alias=term { inClause.setKeyAlias(key_alias.getText()); }
+           K_IN '(' f1=term { inClause.andKeyEquals(f1); }
+                  (',' fN=term { inClause.andKeyEquals(fN); } )* ')'
         { $clause = inClause; }
     ;
 
@@ -212,12 +213,12 @@ insertStatement returns [UpdateStatement
           List<Term> columnValues = new ArrayList<Term>();
       }
       K_INSERT K_INTO columnFamily=( IDENT | STRING_LITERAL | INTEGER )
-          '(' K_KEY    ( ',' column_name=term  { 
columnNames.add($column_name.item); } )+ ')'
+          '(' key_alias=term ( ',' column_name=term  { 
columnNames.add($column_name.item); } )+ ')'
         K_VALUES
           '(' key=term ( ',' column_value=term { 
columnValues.add($column_value.item); })+ ')'
         ( usingClause[attrs] )?
       {
-          return new UpdateStatement($columnFamily.text, columnNames, 
columnValues, Collections.singletonList(key), attrs);
+          return new UpdateStatement($columnFamily.text, key_alias.getText(), 
columnNames, columnValues, Collections.singletonList(key), attrs);
       }
     ;
 
@@ -298,11 +299,11 @@ updateStatement returns [UpdateStatement
       K_UPDATE columnFamily=( IDENT | STRING_LITERAL | INTEGER )
           ( usingClause[attrs] )?
           K_SET termPairWithOperation[columns] (',' 
termPairWithOperation[columns])*
-          K_WHERE ( K_KEY '=' key=term { keyList = 
Collections.singletonList(key); }
-                    |
-                    K_KEY K_IN '(' keys=termList { keyList = $keys.items; } 
')' )
+          K_WHERE ( key_alias=term ('=' key=term { keyList = 
Collections.singletonList(key); }
+                                    |
+                                    K_IN '(' keys=termList { keyList = 
$keys.items; } ')' ))
       {
-          return new UpdateStatement($columnFamily.text, columns, keyList, 
attrs);
+          return new UpdateStatement($columnFamily.text, key_alias.getText(), 
columns, keyList, attrs);
       }
     ;
 
@@ -326,11 +327,11 @@ deleteStatement returns [DeleteStatement
           ( cols=termList { columnsList = $cols.items; })?
           K_FROM columnFamily=( IDENT | STRING_LITERAL | INTEGER )
           ( K_USING K_CONSISTENCY K_LEVEL { cLevel = 
ConsistencyLevel.valueOf($K_LEVEL.text); } )?
-          K_WHERE ( K_KEY '=' key=term           { keyList = 
Collections.singletonList(key); }
-                  | K_KEY K_IN '(' keys=termList { keyList = $keys.items; } ')'
+          K_WHERE ( key_alias=term ('=' key=term           { keyList = 
Collections.singletonList(key); }
+                                   | K_IN '(' keys=termList { keyList = 
$keys.items; } ')')
                   )?
       {
-          return new DeleteStatement(columnsList, $columnFamily.text, cLevel, 
keyList);
+          return new DeleteStatement(columnsList, $columnFamily.text, 
key_alias.getText(), cLevel, keyList);
       }
     ;
 
@@ -366,7 +367,7 @@ createColumnFamilyStatement returns [Cre
 
 createCfamColumns[CreateColumnFamilyStatement expr]
     : n=term v=createCfamColumnValidator { $expr.addColumn(n, $v.validator); }
-    | K_KEY v=createCfamColumnValidator K_PRIMARY K_KEY { 
$expr.setKeyType($v.validator); }
+    | k=term v=createCfamColumnValidator K_PRIMARY K_KEY { 
$expr.setKeyAlias(k.getText()); $expr.setKeyType($v.validator); }
     ;
 
 createCfamColumnValidator returns [String validator]
@@ -451,9 +452,8 @@ termPairWithOperation[Map<Term, Operatio
 
 // Note: ranges are inclusive so >= and >, and < and <= all have the same 
semantics.  
 relation returns [Relation rel]
-    : { Term entity = new Term("KEY", STRING_LITERAL); }
-      (name=term { entity = $name.item; } ) type=('=' | '<' | '<=' | '>=' | 
'>') t=term
-      { return new Relation(entity, $type.text, $t.item); }
+    : name=term type=('=' | '<' | '<=' | '>=' | '>') t=term
+      { return new Relation($name.item, $type.text, $t.item); }
     ;
 
 // TRUNCATE <CF>;

Modified: 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
 (original)
+++ 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/CreateColumnFamilyStatement.java
 Sat Jun 11 01:45:16 2011
@@ -35,6 +35,7 @@ import org.apache.cassandra.db.ColumnFam
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.db.marshal.TypeParser;
 import org.apache.cassandra.thrift.InvalidRequestException;
+import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.FBUtilities;
 
 /** A <code>CREATE COLUMNFAMILY</code> parsed from a CQL query statement. */
@@ -95,7 +96,8 @@ public class CreateColumnFamilyStatement
     private final Map<Term, String> columns = new HashMap<Term, String>();
     private final Map<String, String> properties = new HashMap<String, 
String>();
     private List<String> keyValidator = new ArrayList<String>();
-    
+    private ByteBuffer keyAlias = null;
+
     public CreateColumnFamilyStatement(String name)
     {
         this.name = name;
@@ -180,7 +182,14 @@ public class CreateColumnFamilyStatement
     {
         return keyValidator.get(0);
     }
-    
+
+    public void setKeyAlias(String alias)
+    {
+        // if we got KEY in input we don't need to set an alias
+        if (!alias.toUpperCase().equals("KEY"))
+            keyAlias = ByteBufferUtil.bytes(alias);
+    }
+
     /** Map a keyword to the corresponding value */
     public void addProperty(String name, String value)
     {
@@ -268,7 +277,8 @@ public class CreateColumnFamilyStatement
                    .mergeShardsChance(0.0)
                    .columnMetadata(getColumns(comparator))
                    
.keyValidator(TypeParser.parse(comparators.get(getKeyType())))
-                   
.rowCacheProvider(FBUtilities.newCacheProvider(getPropertyString(KW_ROW_CACHE_PROVIDER,
 CFMetaData.DEFAULT_ROW_CACHE_PROVIDER)));
+                   
.rowCacheProvider(FBUtilities.newCacheProvider(getPropertyString(KW_ROW_CACHE_PROVIDER,
 CFMetaData.DEFAULT_ROW_CACHE_PROVIDER)))
+                   .keyAlias(keyAlias);
         }
         catch (ConfigurationException e)
         {

Modified: 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/DeleteStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/DeleteStatement.java?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/DeleteStatement.java
 (original)
+++ 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/DeleteStatement.java
 Sat Jun 11 01:45:16 2011
@@ -47,9 +47,9 @@ public class DeleteStatement extends Abs
     private List<Term> columns;
     private List<Term> keys;
     
-    public DeleteStatement(List<Term> columns, String columnFamily, 
ConsistencyLevel cLevel, List<Term> keys)
+    public DeleteStatement(List<Term> columns, String columnFamily, String 
keyName, ConsistencyLevel cLevel, List<Term> keys)
     {
-        super(columnFamily, cLevel, null, 0);
+        super(columnFamily, keyName, cLevel, null, 0);
 
         this.columns = columns;
         this.keys = keys;
@@ -102,6 +102,7 @@ public class DeleteStatement extends Abs
     public void mutationForKey(RowMutation mutation, String keyspace, Long 
timestamp) throws InvalidRequestException
     {
         CFMetaData metadata = validateColumnFamily(keyspace, columnFamily);
+        QueryProcessor.validateKeyAlias(metadata, keyName);
 
         AbstractType comparator = metadata.getComparatorFor(null);
 

Modified: 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
 (original)
+++ 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/QueryProcessor.java
 Sat Jun 11 01:45:16 2011
@@ -65,6 +65,8 @@ public class QueryProcessor
 
     private static final long timeLimitForSchemaAgreement = 10 * 1000;
 
+    public static final String DEFAULT_KEY_NAME = 
bufferToString(CFMetaData.DEFAULT_KEY_NAME);
+
     private static List<org.apache.cassandra.db.Row> getSlice(String keyspace, 
SelectStatement select)
     throws InvalidRequestException, TimedOutException, UnavailableException
     {
@@ -401,6 +403,14 @@ public class QueryProcessor
         }
     }
 
+    public static void validateKeyAlias(CFMetaData cfm, String key) throws 
InvalidRequestException
+    {
+        assert key.toUpperCase().equals(key); // should always be uppercased 
by caller
+        String realKeyAlias = bufferToString(cfm.getKeyName()).toUpperCase();
+        if (!realKeyAlias.equals(key))
+            throw new InvalidRequestException(String.format("Expected key '%s' 
to be present in WHERE clause for '%s'", key, cfm.cfName));
+    }
+
     private static void validateColumnNames(Iterable<ByteBuffer> columns)
     throws InvalidRequestException
     {
@@ -500,8 +510,15 @@ public class QueryProcessor
                 SelectStatement select = (SelectStatement)statement.statement;
                 clientState.hasColumnFamilyAccess(select.getColumnFamily(), 
Permission.READ);
                 metadata = validateColumnFamily(keyspace, 
select.getColumnFamily());
+
+                // need to do this in here because we need a CFMD.getKeyName()
+                select.extractKeyAliasFromColumns(metadata);
+
+                if (select.getKeys().size() > 0)
+                    validateKeyAlias(metadata, select.getKeyAlias());
+
                 validateSelect(keyspace, select);
-                
+
                 List<org.apache.cassandra.db.Row> rows;
 
                 // By-key
@@ -969,4 +986,16 @@ public class QueryProcessor
 
         throw new SchemaDisagreementException();
     }
+
+    private static String bufferToString(ByteBuffer string)
+    {
+        try
+        {
+            return ByteBufferUtil.string(string);
+        }
+        catch (CharacterCodingException e)
+        {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
 }

Modified: 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java
 (original)
+++ 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/SelectStatement.java
 Sat Jun 11 01:45:16 2011
@@ -23,6 +23,7 @@ package org.apache.cassandra.cql;
 import java.nio.ByteBuffer;
 import java.util.List;
 
+import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.thrift.ConsistencyLevel;
@@ -142,6 +143,16 @@ public class SelectStatement
         return clause.includeFinishKey();
     }
 
+    public String getKeyAlias()
+    {
+        return clause.getKeyAlias();
+    }
+
+    public void extractKeyAliasFromColumns(CFMetaData cfm)
+    {
+        clause.extractKeysFromColumns(cfm);
+    }
+
     public AbstractType getComparator(String keyspace)
     {
         return DatabaseDescriptor.getComparator(keyspace, columnFamily);

Modified: 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/UpdateStatement.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/UpdateStatement.java?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/UpdateStatement.java
 (original)
+++ 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/UpdateStatement.java
 Sat Jun 11 01:45:16 2011
@@ -49,22 +49,24 @@ public class UpdateStatement extends Abs
     private Map<Term, Operation> columns;
     private List<Term> columnNames, columnValues;
     private List<Term> keys;
-    
+
     /**
      * Creates a new UpdateStatement from a column family name, columns map, 
consistency
      * level, and key term.
      * 
      * @param columnFamily column family name
+     * @param keyName alias key name
      * @param columns a map of column name/values pairs
      * @param keys the keys to update
      * @param attrs additional attributes for statement (CL, timestamp, 
timeToLive)
      */
     public UpdateStatement(String columnFamily,
+                           String keyName,
                            Map<Term, Operation> columns,
                            List<Term> keys,
                            Attributes attrs)
     {
-        super(columnFamily, attrs);
+        super(columnFamily, keyName, attrs);
 
         this.columns = columns;
         this.keys = keys;
@@ -76,18 +78,20 @@ public class UpdateStatement extends Abs
      * alternate update format, <code>INSERT</code>.
      * 
      * @param columnFamily column family name
+     * @param keyName alias key name
      * @param columnNames list of column names
      * @param columnValues list of column values (corresponds to names)
      * @param keys the keys to update
      * @param attrs additional attributes for statement (CL, timestamp, 
timeToLive)
      */
     public UpdateStatement(String columnFamily,
+                           String keyName,
                            List<Term> columnNames,
                            List<Term> columnValues,
                            List<Term> keys,
                            Attributes attrs)
     {
-        super(columnFamily, attrs);
+        super(columnFamily, keyName, attrs);
 
         this.columnNames = columnNames;
         this.columnValues = columnValues;
@@ -139,6 +143,8 @@ public class UpdateStatement extends Abs
 
         CFMetaData metadata = validateColumnFamily(keyspace, columnFamily, 
hasCommutativeOperation);
 
+        QueryProcessor.validateKeyAlias(metadata, keyName);
+
         // Avoid unnecessary authorizations.
         if (!(cfamsSeen.contains(columnFamily)))
         {

Modified: 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java
 (original)
+++ 
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/cql/WhereClause.java
 Sat Jun 11 01:45:16 2011
@@ -21,7 +21,15 @@ package org.apache.cassandra.cql;
  */
 
 
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.thrift.InvalidRequestException;
+import org.apache.cassandra.thrift.ThriftValidation;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -30,10 +38,13 @@ import java.util.List;
  */
 public class WhereClause
 {
+    // added to either by the parser, e.g. from an IN clause, or by 
extractKeysFromColumns
     private List<Term> keys = new ArrayList<Term>();
     private Term startKey, finishKey;
     private List<Relation> columns = new ArrayList<Relation>();
     private boolean includeStartKey = false, includeFinishKey = false;
+    // set by extractKeysFromColumns
+    private String keyAlias = null;
 
     /**
      * Create a new WhereClause with the first parsed relation.
@@ -96,11 +107,6 @@ public class WhereClause
         return startKey != null;
     }
     
-    public boolean isKeyList()
-    {
-        return !isKeyRange();
-    }
-    
     public Term getStartKey()
     {
         return startKey;
@@ -125,4 +131,47 @@ public class WhereClause
     {
         return includeFinishKey;
     }
+
+    public void setKeyAlias(String alias)
+    {
+        keyAlias = alias.toUpperCase();
+    }
+
+    public String getKeyAlias()
+    {
+        // TODO fix special casing here, key alias should always be set 
post-extract
+        // key alias as not related to keys in here, it can be unset when we 
have a query like
+        // SELECT * FROM <CF> WHERE key = 1 and col > 2 and col < 3;
+        // it will be always set when statement looks like this
+        // SELECT * FROM <CF> WHERE <key> IN (.., .., ..);
+        // key is NULL when KEY keyword is used or when key alias given by 
user was not recognized
+        // validateKeyAlias will throw an exception for us in that case
+        return keyAlias == null ? QueryProcessor.DEFAULT_KEY_NAME : keyAlias;
+    }
+
+    public void extractKeysFromColumns(CFMetaData cfm)
+    {
+        ByteBuffer realKeyAlias = cfm.getKeyName();
+
+        if (!keys.isEmpty())
+            return; // we already have key(s) set
+
+        Iterator<Relation> iter = columns.iterator();
+
+        while (iter.hasNext())
+        {
+            Relation relation = iter.next();
+
+            ByteBuffer name = 
ByteBufferUtil.bytes(relation.getEntity().getText());
+
+            if (name.equals(realKeyAlias))
+            {
+                // setting found key as an alias
+                keyAlias = relation.getEntity().getText().toUpperCase();
+                keys.add(relation.getValue()); // add a key value to the keys 
list
+                iter.remove(); // removing it from the columns
+                break;
+            }
+        }
+    }
 }

Modified: cassandra/branches/cassandra-0.8/test/system/test_cql.py
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/test/system/test_cql.py?rev=1134502&r1=1134501&r2=1134502&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/test/system/test_cql.py (original)
+++ cassandra/branches/cassandra-0.8/test/system/test_cql.py Sat Jun 11 
01:45:16 2011
@@ -1185,3 +1185,90 @@ class TestCql(ThriftTester):
         assert_raises(cql.ProgrammingError,
                       cursor.execute,
                       "UPDATE CounterCF SET count_me = count_not_me + 2 WHERE 
key = 'counter1'")
+
+    def test_key_alias_support(self):
+        "should be possible to use alias instead of KEY keyword"
+        cursor = init()
+
+        cursor.execute("""
+               CREATE SCHEMA KeyAliasKeyspace WITH 
strategy_options:replication_factor = '1'
+                   AND strategy_class = 'SimpleStrategy';
+        """)
+        cursor.execute("USE KeyAliasKeyspace;")
+
+        # create a Column Family with key alias
+        cursor.execute("""
+            CREATE COLUMNFAMILY KeyAliasCF (
+                'id' varint PRIMARY KEY,
+                'username' text
+            ) WITH comment = 'shiny, new, cf' AND default_validation = ascii;
+        """)
+
+        # TODO: temporary (until this can be done with CQL).
+        ksdef = thrift_client.describe_keyspace("KeyAliasKeyspace")
+        cfdef = ksdef.cf_defs[0]
+
+        assert len(ksdef.cf_defs) == 1, \
+            "expected 1 column family total, found %d" % len(ksdef.cf_defs)
+        assert cfdef.key_alias == 'id', "expected 'id' alias, got %s" % 
cfdef.key_alias
+
+        # try do insert/update
+        cursor.execute("INSERT INTO KeyAliasCF (id, username) VALUES (1, 
jbellis)")
+
+        # check if we actually stored anything
+        cursor.execute("SELECT * FROM KeyAliasCF WHERE id = 1")
+        assert cursor.rowcount == 1, "expected 1 results, got %d" % 
cursor.rowcount
+        colnames = [col_d[0] for col_d in cursor.description]
+        assert len(colnames) == 2
+
+        r = cursor.fetchone()
+        assert len(r) == 2, "expected 2, got %d" % len(r)
+        assert r[0] == 1
+        assert r[1] == 'jbellis'
+
+        cursor.execute("UPDATE KeyAliasCF SET username = 'xedin' WHERE id = 2")
+
+        # check if we actually stored anything
+        cursor.execute("SELECT * FROM KeyAliasCF WHERE id = 2")
+        assert cursor.rowcount == 1, "expected 1 results, got %d" % 
cursor.rowcount
+        colnames = [col_d[0] for col_d in cursor.description]
+        assert len(colnames) == 2
+
+        r = cursor.fetchone()
+        assert len(r) == 2, "expected 2, got %d" % len(r)
+        assert r[0] == 2
+        assert r[1] == 'xedin'
+
+        # delete with key alias
+        cursor.execute("DELETE FROM KeyAliasCF WHERE id = 2")
+        # check if we actually stored anything
+        cursor.execute("SELECT * FROM KeyAliasCF WHERE id = 2")
+        assert cursor.rowcount == 1, "expected 1 results, got %d" % 
cursor.rowcount
+
+        r = cursor.fetchone()
+        assert len(r) == 1, "expected 1, got %s" % r
+        assert r[0] == 2, "expected id = 2, got %d" % r[0]
+
+        # if alias was set you can't use KEY keyword anymore
+        assert_raises(cql.ProgrammingError,
+                      cursor.execute,
+                      "INSERT INTO KeyAliasCF (KEY, username) VALUES (6, 
jbellis)")
+
+        assert_raises(cql.ProgrammingError,
+                      cursor.execute,
+                      "UPDATE KeyAliasCF SET username = 'xedin' WHERE KEY = 7")
+
+        assert_raises(cql.ProgrammingError,
+                      cursor.execute,
+                      "DELETE FROM KeyAliasCF WHERE KEY = 2")
+
+        assert_raises(cql.ProgrammingError,
+                      cursor.execute,
+                      "SELECT * FROM KeyAliasCF WHERE KEY = 2")
+
+        assert_raises(cql.ProgrammingError,
+                      cursor.execute,
+                      "SELECT * FROM KeyAliasCF WHERE KEY IN (1, 2)")
+
+        cursor.execute("USE Keyspace1")
+        cursor.execute("DROP KEYSPACE KeyAliasKeyspace")


Reply via email to