cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jbel...@apache.org
Subject svn commit: r1096804 - in /cassandra/trunk: ./ doc/cql/ src/java/org/apache/cassandra/cql/ test/system/
Date Tue, 26 Apr 2011 17:07:21 GMT
Author: jbellis
Date: Tue Apr 26 17:07:20 2011
New Revision: 1096804

URL: http://svn.apache.org/viewvc?rev=1096804&view=rev
Log:
add support for IN to cql SELECT, UPDATE
patch by Pavel Yaskevich; reviewed by jbellis for CASSANDRA-2553

Modified:
    cassandra/trunk/CHANGES.txt
    cassandra/trunk/doc/cql/CQL.textile
    cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g
    cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/UpdateStatement.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/WhereClause.java
    cassandra/trunk/test/system/test_cql.py

Modified: cassandra/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/cassandra/trunk/CHANGES.txt?rev=1096804&r1=1096803&r2=1096804&view=diff
==============================================================================
--- cassandra/trunk/CHANGES.txt (original)
+++ cassandra/trunk/CHANGES.txt Tue Apr 26 17:07:20 2011
@@ -1,9 +1,13 @@
+1.0-dev
+ * add support for insert, delete in cql BATCH (CASSANDRA-2537)
+ * add support for IN to cql SELECT, UPDATE (CASSANDRA-2553)
+
+
 0.8.0-?
  * fix NPE compacting index CFs (CASSANDRA-2528)
  * Remove checking all column families on startup for compaction candidates (CASSANDRA-2444)
  * validate CQL create keyspace options (CASSANDRA-2525)
  * fix nodetool setcompactionthroughput (CASSANDRA-2550)
- * add support for insert, delete in cql BATCH (CASSANDRA-2537)
 
 
 0.8.0-beta1

Modified: cassandra/trunk/doc/cql/CQL.textile
URL: http://svn.apache.org/viewvc/cassandra/trunk/doc/cql/CQL.textile?rev=1096804&r1=1096803&r2=1096804&view=diff
==============================================================================
--- cassandra/trunk/doc/cql/CQL.textile (original)
+++ cassandra/trunk/doc/cql/CQL.textile Tue Apr 26 17:07:20 2011
@@ -56,6 +56,7 @@ 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>', ...)
 
 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__.
 
@@ -96,6 +97,7 @@ 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>',
...)
 
 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.
 

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=1096804&r1=1096803&r2=1096804&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g Tue Apr 26 17:07:20 2011
@@ -121,14 +121,16 @@ useStatement returns [String keyspace]
     ;
 
 /**
- * SELECT FROM
+ * SELECT
+ *  (REVERSED)? <expression>
+ * FROM
  *     <CF>
  * USING
- *     CONSISTENCY.ONE
+ *     CONSISTENCY <LEVEL>
  * WHERE
  *     KEY = "key1" AND KEY = "key2" AND
  *     COL > 1 AND COL < 100
- * COLLIMIT 10 DESC;
+ * LIMIT <NUMBER>;
  */
 selectStatement returns [SelectStatement expr]
     : { 
@@ -174,8 +176,14 @@ selectExpression returns [SelectExpressi
 
 // relation [[AND relation] ...]
 whereClause returns [WhereClause clause]
+    @init {
+        WhereClause inClause = new WhereClause();
+    }
     : 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); } )* ')'
+        { $clause = inClause; }
     ;
 
 /**
@@ -203,7 +211,7 @@ insertStatement returns [UpdateStatement
           '(' key=term ( ',' column_value=term { columnValues.add($column_value.item); })+
')'
         ( K_USING K_CONSISTENCY K_LEVEL { cLevel = ConsistencyLevel.valueOf($K_LEVEL.text);
} )?
       {
-          return new UpdateStatement($columnFamily.text, cLevel, columnNames, columnValues,
key);
+          return new UpdateStatement($columnFamily.text, cLevel, columnNames, columnValues,
Collections.singletonList(key));
       }
     ;
 
@@ -265,13 +273,16 @@ updateStatement returns [UpdateStatement
     : {
           ConsistencyLevel cLevel = null;
           Map<Term, Term> columns = new HashMap<Term, Term>();
+          List<Term> keyList = null;
       }
       K_UPDATE columnFamily=( IDENT | STRING_LITERAL | INTEGER )
           (K_USING K_CONSISTENCY K_LEVEL { cLevel = ConsistencyLevel.valueOf($K_LEVEL.text);
})?
           K_SET termPair[columns] (',' termPair[columns])*
-          K_WHERE K_KEY '=' key=term
+          K_WHERE ( K_KEY '=' key=term { keyList = Collections.singletonList(key); }
+                    |
+                    K_KEY K_IN '(' keys=termList { keyList = $keys.items; } ')' )
       {
-          return new UpdateStatement($columnFamily.text, cLevel, columns, key);
+          return new UpdateStatement($columnFamily.text, cLevel, columns, keyList);
       }
     ;
 

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=1096804&r1=1096803&r2=1096804&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java Tue Apr 26 17:07:20
2011
@@ -72,20 +72,15 @@ import static org.apache.cassandra.thrif
 public class QueryProcessor
 {
     private static final Logger logger = LoggerFactory.getLogger(QueryProcessor.class);
-    
+
     private static List<org.apache.cassandra.db.Row> getSlice(String keyspace, SelectStatement
select)
     throws InvalidRequestException, TimedOutException, UnavailableException
     {
-        List<org.apache.cassandra.db.Row> rows = null;
         QueryPath queryPath = new QueryPath(select.getColumnFamily());
         AbstractType<?> comparator = select.getComparator(keyspace);
         List<ReadCommand> commands = new ArrayList<ReadCommand>();
-        
-        assert select.getKeys().size() == 1;
-        
+
         CFMetaData metadata = validateColumnFamily(keyspace, select.getColumnFamily(), false);
-        ByteBuffer key = select.getKeys().get(0).getByteBuffer(metadata.getKeyValidator());
-        validateKey(key);
 
         // ...of a list of column names
         if (!select.isColumnRange())
@@ -93,29 +88,42 @@ public class QueryProcessor
             Collection<ByteBuffer> columnNames = new ArrayList<ByteBuffer>();
             for (Term column : select.getColumnNames())
                 columnNames.add(column.getByteBuffer(comparator));
-            
+
             validateColumnNames(columnNames);
-            commands.add(new SliceByNamesReadCommand(keyspace, key, queryPath, columnNames));
+
+            for (Term rawKey: select.getKeys())
+            {
+                ByteBuffer key = rawKey.getByteBuffer(metadata.getKeyValidator());
+
+                validateKey(key);
+                commands.add(new SliceByNamesReadCommand(keyspace, key, queryPath, columnNames));
+            }
         }
         // ...a range (slice) of column names
         else
         {
             ByteBuffer start = select.getColumnStart().getByteBuffer(comparator);
             ByteBuffer finish = select.getColumnFinish().getByteBuffer(comparator);
-            
-            validateSliceRange(metadata, start, finish, select.isColumnsReversed());
-            commands.add(new SliceFromReadCommand(keyspace,
-                                                  key,
-                                                  queryPath,
-                                                  start,
-                                                  finish,
-                                                  select.isColumnsReversed(),
-                                                  select.getColumnsLimit()));
+
+            for (Term rawKey : select.getKeys())
+            {
+                ByteBuffer key = rawKey.getByteBuffer(metadata.getKeyValidator());
+
+                validateKey(key);
+                validateSliceRange(metadata, start, finish, select.isColumnsReversed());
+                commands.add(new SliceFromReadCommand(keyspace,
+                                                      key,
+                                                      queryPath,
+                                                      start,
+                                                      finish,
+                                                      select.isColumnsReversed(),
+                                                      select.getColumnsLimit()));
+            }
         }
 
         try
         {
-            rows = StorageProxy.read(commands, select.getConsistencyLevel());
+            return StorageProxy.read(commands, select.getConsistencyLevel());
         }
         catch (TimeoutException e)
         {
@@ -125,15 +133,11 @@ public class QueryProcessor
         {
             throw new RuntimeException(e);
         }
-        
-        return rows;
     }
-    
+
     private static List<org.apache.cassandra.db.Row> multiRangeSlice(String keyspace,
SelectStatement select)
     throws TimedOutException, UnavailableException, InvalidRequestException
     {
-        List<org.apache.cassandra.db.Row> rows = null;
-        
         AbstractType<?> keyType = DatabaseDescriptor.getCFMetaData(keyspace,
                                                                    select.getColumnFamily()).getKeyValidator();
         ByteBuffer startKey = (select.getKeyStart() != null) ? select.getKeyStart().getByteBuffer(keyType)
: (new Term()).getByteBuffer();
@@ -149,13 +153,13 @@ public class QueryProcessor
 
         try
         {
-            rows = StorageProxy.getRangeSlice(new RangeSliceCommand(keyspace,
+            return StorageProxy.getRangeSlice(new RangeSliceCommand(keyspace,
                                                                     select.getColumnFamily(),
                                                                     null,
                                                                     thriftSlicePredicate,
                                                                     bounds,
                                                                     select.getNumRecords()),
-                                              select.getConsistencyLevel());
+                                                                    select.getConsistencyLevel());
         }
         catch (IOException e)
         {
@@ -169,8 +173,6 @@ public class QueryProcessor
         {
             throw new TimedOutException();
         }
-        
-        return rows;
     }
     
     private static List<org.apache.cassandra.db.Row> getIndexedSlices(String keyspace,
SelectStatement select)
@@ -229,29 +231,14 @@ public class QueryProcessor
 
         for (UpdateStatement update : updateStatements)
         {
-            CFMetaData metadata = validateColumnFamily(keyspace, update.getColumnFamily(),
false);
             // Avoid unnecessary authorizations.
             if (!(cfamsSeen.contains(update.getColumnFamily())))
             {
                 clientState.hasColumnFamilyAccess(update.getColumnFamily(), Permission.WRITE);
                 cfamsSeen.add(update.getColumnFamily());
             }
-            
-            ByteBuffer key = update.getKey().getByteBuffer(update.getKeyType(keyspace));
-            validateKey(key);
-            AbstractType<?> comparator = update.getComparator(keyspace);
-            
-            RowMutation rm = new RowMutation(keyspace, key);
-            for (Map.Entry<Term, Term> column : update.getColumns().entrySet())
-            {
-                ByteBuffer colName = column.getKey().getByteBuffer(comparator);
-                ByteBuffer colValue = column.getValue().getByteBuffer(update.getValueValidator(keyspace,
colName));
-                
-                validateColumn(metadata, colName, colValue);
-                rm.add(new QueryPath(update.getColumnFamily(), null, colName), colValue,
System.currentTimeMillis());
-            }
-            
-            rowMutations.add(rm);
+
+            rowMutations.addAll(update.prepareRowMutations(keyspace, clientState));
         }
         
         try
@@ -311,10 +298,6 @@ public class QueryProcessor
         if (select.isKeyRange() && (select.getKeyFinish() != null) && (select.getColumnRelations().size()
> 0))
             throw new InvalidRequestException("You cannot combine key range and by-column
clauses in a SELECT");
         
-        // Multiget scenario (KEY = foo AND KEY = bar ...)
-        if (select.getKeys().size() > 1)
-            throw new InvalidRequestException("SELECTs can contain only by by-key clause");
-        
         AbstractType<?> comparator = select.getComparator(keyspace);
         
         if (select.getColumnRelations().size() > 0)
@@ -481,13 +464,13 @@ public class QueryProcessor
                 comparator = metadata.getComparatorFor(null);
                 validateSelect(keyspace, select);
                 
-                List<org.apache.cassandra.db.Row> rows = null;
+                List<org.apache.cassandra.db.Row> rows;
 
                 // By-key
                 if (!select.isKeyRange() && (select.getKeys().size() > 0))
                 {
                     rows = getSlice(keyspace, select);
-                    
+
                     // Only return the column count, (of the at-most 1 row).
                     if (select.isCountOperation())
                     {

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=1096804&r1=1096803&r2=1096804&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/UpdateStatement.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/UpdateStatement.java Tue Apr 26 17:07:20
2011
@@ -46,7 +46,7 @@ public class UpdateStatement extends Abs
 {
     private Map<Term, Term> columns;
     private List<Term> columnNames, columnValues;
-    private Term key;
+    private List<Term> keys;
     
     /**
      * Creates a new UpdateStatement from a column family name, columns map, consistency
@@ -55,14 +55,14 @@ public class UpdateStatement extends Abs
      * @param columnFamily column family name
      * @param cLevel the thrift consistency level
      * @param columns a map of column name/values pairs
-     * @param key the key name
+     * @param keys the keys to update
      */
-    public UpdateStatement(String columnFamily, ConsistencyLevel cLevel, Map<Term, Term>
columns, Term key)
+    public UpdateStatement(String columnFamily, ConsistencyLevel cLevel, Map<Term, Term>
columns, List<Term> keys)
     {
         super(columnFamily, cLevel);
 
         this.columns = columns;
-        this.key = key;
+        this.keys = keys;
     }
 
     /**
@@ -71,11 +71,11 @@ public class UpdateStatement extends Abs
      * 
      * @param columnFamily column family name
      * @param columns a map of column name/values pairs
-     * @param key the key name
+     * @param keys the keys to update
      */
-    public UpdateStatement(String columnFamily, Map<Term, Term> columns, Term key)
+    public UpdateStatement(String columnFamily, Map<Term, Term> columns, List<Term>
keys)
     {
-        this(columnFamily, null, columns, key);
+        this(columnFamily, null, columns, keys);
     }
     
     /**
@@ -87,15 +87,15 @@ public class UpdateStatement extends Abs
      * @param cLevel the thrift consistency level
      * @param columnNames list of column names
      * @param columnValues list of column values (corresponds to names)
-     * @param key the key name
+     * @param keys the keys to update
      */
-    public UpdateStatement(String columnFamily, ConsistencyLevel cLevel, List<Term>
columnNames, List<Term> columnValues, Term key)
+    public UpdateStatement(String columnFamily, ConsistencyLevel cLevel, List<Term>
columnNames, List<Term> columnValues, List<Term> keys)
     {
         super(columnFamily, cLevel);
 
         this.columnNames = columnNames;
         this.columnValues = columnValues;
-        this.key = key;
+        this.keys = keys;
     }
 
     /**
@@ -133,8 +133,31 @@ public class UpdateStatement extends Abs
             cfamsSeen.add(columnFamily);
         }
 
-        ByteBuffer key = this.key.getByteBuffer(getKeyType(keyspace));
+        List<RowMutation> rowMutations = new LinkedList<RowMutation>();
+
+        for (Term key: keys)
+        {
+            rowMutations.add(mutationForKey(keyspace, key.getByteBuffer(getKeyType(keyspace)),
metadata));
+        }
+
+        return rowMutations;
+    }
+
+    /**
+     * Compute a row mutation for a single key
+     *
+     * @param keyspace working keyspace
+     * @param key key to change
+     * @param metadata information about CF
+     *
+     * @return row mutation
+     *
+     * @throws InvalidRequestException on the wrong request
+     */
+    private RowMutation mutationForKey(String keyspace, ByteBuffer key, CFMetaData metadata)
throws InvalidRequestException
+    {
         validateKey(key);
+
         AbstractType<?> comparator = getComparator(keyspace);
 
         RowMutation rm = new RowMutation(keyspace, key);
@@ -147,7 +170,7 @@ public class UpdateStatement extends Abs
             rm.add(new QueryPath(columnFamily, null, colName), colValue, System.currentTimeMillis());
         }
 
-        return Arrays.asList(rm);
+        return rm;
     }
 
     public String getColumnFamily()
@@ -155,9 +178,9 @@ public class UpdateStatement extends Abs
         return columnFamily;
     }
     
-    public Term getKey()
+    public List<Term> getKeys()
     {
-        return key;
+        return keys;
     }
     
     public Map<Term, Term> getColumns() throws InvalidRequestException
@@ -184,9 +207,9 @@ public class UpdateStatement extends Abs
     
     public String toString()
     {
-        return String.format("UpdateStatement(columnFamily=%s, key=%s, columns=%s, consistency=%s)",
+        return String.format("UpdateStatement(columnFamily=%s, keys=%s, columns=%s, consistency=%s)",
                              columnFamily,
-                             key,
+                             keys,
                              columns,
                              cLevel);
     }

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=1096804&r1=1096803&r2=1096804&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/WhereClause.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/WhereClause.java Tue Apr 26 17:07:20
2011
@@ -69,7 +69,16 @@ public class WhereClause
         else
             columns.add(relation);
     }
-    
+
+    /**
+     * The same as KEY = <key> to avoid using Relation object
+     * @param key key to include into clause
+     */
+    public void andKeyEquals(Term key)
+    {
+        keys.add(key);
+    }
+
     public List<Relation> getColumnRelations()
     {
         return columns;

Modified: cassandra/trunk/test/system/test_cql.py
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/system/test_cql.py?rev=1096804&r1=1096803&r2=1096804&view=diff
==============================================================================
--- cassandra/trunk/test/system/test_cql.py (original)
+++ cassandra/trunk/test/system/test_cql.py Tue Apr 26 17:07:20 2011
@@ -233,13 +233,6 @@ class TestCql(ThriftTester):
         assert r[1] == "3"
         assert r[2] == "2"
 
-    def test_error_on_multiple_key_by(self):
-        "ensure multiple key-bys in where clause excepts"
-        cursor = init()
-        assert_raises(cql.ProgrammingError, cursor.execute, """
-            SELECT 'col' FROM StandardString1 WHERE KEY = 'ka' AND KEY = 'kb';
-        """)
-
     def test_index_scan_equality(self):
         "indexed scan where column equals value"
         cursor = init()
@@ -756,3 +749,29 @@ class TestCql(ThriftTester):
                           DELETE 'name' FROM StandardString1 WHERE KEY = 'bKey4'
                       APPLY BATCH
                       """)
+
+    def test_multiple_keys_on_select_and_update(self):
+        "select/update statements should support multiple keys by KEY IN construction"
+        cursor = init()
+        cursor.compression = 'NONE'
+
+        # inserting the same data to the multiple keys
+        cursor.execute("""
+          UPDATE StandardString1 USING CONSISTENCY ONE SET password = 'p4ssw0rd', login =
'same' WHERE KEY IN ('mUser1', 'mUser2')
+        """)
+
+        cursor.execute("SELECT * FROM StandardString1 WHERE KEY IN ('mUser1', 'mUser2')")
+        assert cursor.rowcount == 2, "expected 2 results, got %d" % cursor.rowcount
+        colnames = [col_d[0] for col_d in cursor.description]
+
+        assert colnames[1] == "login", \
+               "unrecognized name '%s'" % colnames[1]
+        assert colnames[2] == "password", \
+               "unrecognized name '%s'" % colnames[2]
+
+        for i in range(2):
+            r = cursor.fetchone()
+            assert r[1] == "same", \
+                   "unrecognized value '%s'" % r[1]
+            assert r[2] == "p4ssw0rd", \
+                   "unrecognized value '%s'" % r[1]



Mime
View raw message