You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by xe...@apache.org on 2011/09/07 19:16:47 UTC

svn commit: r1166273 - in /cassandra/trunk: CHANGES.txt doc/cql/CQL.textile src/java/org/apache/cassandra/cql/QueryProcessor.java test/system/test_cql.py

Author: xedin
Date: Wed Sep  7 17:16:46 2011
New Revision: 1166273

URL: http://svn.apache.org/viewvc?rev=1166273&view=rev
Log:
fix of the CQL count() behavior
patch by Jonathan Ellis and Pavel Yaskevich; reviewed by Eric Evans and Pavel Yaskevich for CASSANDRA-3068

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

Modified: cassandra/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/cassandra/trunk/CHANGES.txt?rev=1166273&r1=1166272&r2=1166273&view=diff
==============================================================================
--- cassandra/trunk/CHANGES.txt (original)
+++ cassandra/trunk/CHANGES.txt Wed Sep  7 17:16:46 2011
@@ -61,7 +61,7 @@
  * add ability to use multiple threads during a single compaction
    (CASSANDRA-2901)
  * make AbstractBounds.normalize support overlapping ranges (CASSANDRA-2641)
-
+ * fix of the CQL count() behavior (CASSANDRA-3068)
 
 0.8.5
  * fix NPE when encryption_options is unspecified (CASSANDRA-3007)

Modified: cassandra/trunk/doc/cql/CQL.textile
URL: http://svn.apache.org/viewvc/cassandra/trunk/doc/cql/CQL.textile?rev=1166273&r1=1166272&r2=1166273&view=diff
==============================================================================
--- cassandra/trunk/doc/cql/CQL.textile (original)
+++ cassandra/trunk/doc/cql/CQL.textile Wed Sep  7 17:16:46 2011
@@ -1,4 +1,4 @@
-h1. Cassandra Query Language (CQL) v1.1.1
+h1. Cassandra Query Language (CQL) v2.0
 
 h2. Table of Contents
 
@@ -44,6 +44,13 @@ SELECT ... FROM <COLUMN FAMILY> ...
 
 The @FROM@ clause is used to specify the Cassandra column family applicable to a @SELECT@ query.
 
+bc.
+SELECT count(*) FROM <COLUMN FAMILY> ...
+
+The @count@ aggregate function returns a single row, with a single column "count" whose value is the number of rows from the pre-aggregation resultset.
+
+Currently, @count@ is the only function supported by CQL.
+
 h3. Consistency Level
 
 bc. 
@@ -373,6 +380,9 @@ Versioning of the CQL language adheres t
 h1. Changes
 
 pre. 
+Thu, 07 Sep 2011 09:01:00 -0500 - Jonathan Ellis
+ * Updated version to 2.0; Documented row-based count()
+
 Wed, 10 Aug 2011 11:22:00 -0500 - Eric Evans
  * Improved INSERT vs. UPDATE wording.
  * Documented counter column incr/descr.

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=1166273&r1=1166272&r2=1166273&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java Wed Sep  7 17:16:46 2011
@@ -320,9 +320,6 @@ public class QueryProcessor
     /* Test for SELECT-specific taboos */
     private static void validateSelect(String keyspace, SelectStatement select) throws InvalidRequestException
     {
-        if (select.isCountOperation() && (select.isKeyRange() || select.getKeys().size() < 1))
-            throw new InvalidRequestException("Counts can only be performed for a single record (Hint: KEY=term)");
-        
         // Finish key w/o start key (KEY < foo)
         if (!select.isKeyRange() && (select.getKeyFinish() != null))
             throw new InvalidRequestException("Key range clauses must include a start key (i.e. KEY > term)");
@@ -530,17 +527,6 @@ public class QueryProcessor
                 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())
-                    {
-                        result.type = CqlResultType.INT;
-                        if (rows.size() > 0)
-                            result.setNum(rows.get(0).cf != null ? rows.get(0).cf.getSortedColumns().size() : 0);
-                        else
-                            result.setNum(0);
-                        return result;
-                    }
                 }
                 else
                 {
@@ -555,15 +541,29 @@ public class QueryProcessor
                         rows = getIndexedSlices(keyspace, select);
                     }
                 }
-                
-                List<CqlRow> cqlRows = new ArrayList<CqlRow>();
+
+                // count resultset is a single column named "count"
                 result.type = CqlResultType.ROWS;
+                if (select.isCountOperation())
+                {
+                    validateCountOperation(select);
+
+                    ByteBuffer countBytes = ByteBufferUtil.bytes("count");
+                    result.schema = new CqlMetadata(Collections.<ByteBuffer, String>emptyMap(),
+                                                    Collections.<ByteBuffer, String>emptyMap(),
+                                                    "AsciiType",
+                                                    "LongType");
+                    List<Column> columns = Collections.singletonList(new Column(countBytes).setValue(ByteBufferUtil.bytes((long) rows.size())));
+                    result.rows = Collections.singletonList(new CqlRow(countBytes, columns));
+                    return result;
+                }
+
+                // otherwise create resultset from query results
                 result.schema = new CqlMetadata(new HashMap<ByteBuffer, String>(),
                                                 new HashMap<ByteBuffer, String>(),
                                                 metadata.comparator.toString(),
                                                 TypeParser.getShortName(metadata.getDefaultValidator()));
-
-                // Create the result set
+                List<CqlRow> cqlRows = new ArrayList<CqlRow>();
                 for (org.apache.cassandra.db.Row row : rows)
                 {
                     /// No results for this row
@@ -635,7 +635,7 @@ public class QueryProcessor
                         Collections.reverse(cqlRow.columns);
                     cqlRows.add(cqlRow);
                 }
-                
+
                 result.rows = cqlRows;
                 return result;
 
@@ -1005,6 +1005,23 @@ public class QueryProcessor
         throw new SchemaDisagreementException();
     }
 
+    private static void validateCountOperation(SelectStatement select) throws InvalidRequestException
+    {
+        if (select.isWildcard())
+            return; // valid count(*)
+
+        if (!select.isColumnRange())
+        {
+            List<Term> columnNames = select.getColumnNames();
+            String firstColumn = columnNames.get(0).getText();
+
+            if (columnNames.size() == 1 && (firstColumn.equals("*") || firstColumn.equals("1")))
+                return; // valid count(*) || count(1)
+        }
+
+        throw new InvalidRequestException("Only COUNT(*) and COUNT(1) operations are currently supported.");
+    }
+
     private static String bufferToString(ByteBuffer string)
     {
         try

Modified: cassandra/trunk/test/system/test_cql.py
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/system/test_cql.py?rev=1166273&r1=1166272&r2=1166273&view=diff
==============================================================================
--- cassandra/trunk/test/system/test_cql.py (original)
+++ cassandra/trunk/test/system/test_cql.py Wed Sep  7 17:16:46 2011
@@ -336,11 +336,23 @@ class TestCql(ThriftTester):
     def test_column_count(self):
         "getting a result count instead of results"
         cursor = init()
-        cursor.execute("""
-            SELECT COUNT(1..4) FROM StandardLongA WHERE KEY = 'aa';
-        """)
+        cursor.execute("SELECT COUNT(*) FROM StandardLongA")
+        r = cursor.fetchone()
+        assert r[0] == 7, "expected 7 results, got %d" % (r and r or 0)
+        cursor.execute("SELECT COUNT(1) FROM StandardLongA")
         r = cursor.fetchone()
-        assert r[0] == 4, "expected 4 results, got %d" % (r and r or 0)
+        assert r[0] == 7, "expected 7 results, got %d" % (r and r or 0)
+
+        # count(*) and count(1) are only supported operations
+        assert_raises(cql.ProgrammingError,
+                      cursor.execute,
+                      "SELECT COUNT(name) FROM StandardLongA")
+        assert_raises(cql.ProgrammingError,
+                      cursor.execute,
+                      "SELECT COUNT(1..2) FROM StandardLongA")
+        assert_raises(cql.ProgrammingError,
+                      cursor.execute,
+                      "SELECT COUNT(1, 2, 3) FROM StandardLongA")
 
     def test_truncate_columnfamily(self):
         "truncating a column family"