Repository: cassandra
Updated Branches:
  refs/heads/trunk 0edf54777 -> 86bca3a0b


Allow count(*) and count(1) to be use as normal aggregation

patch by Benjamin Lerer; reviewed by Stefania Alborghetti for CASSANDRA-10114


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

Branch: refs/heads/trunk
Commit: 4fc58513dce5ee6acb83ba07d9f31c26812075f9
Parents: 62fc314
Author: blerer <benjamin.le...@datastax.com>
Authored: Thu Aug 20 14:01:37 2015 +0200
Committer: blerer <benjamin.le...@datastax.com>
Committed: Thu Aug 20 14:01:37 2015 +0200

----------------------------------------------------------------------
 NEWS.txt                                        |  1 +
 src/java/org/apache/cassandra/cql3/Cql.g        |  9 +----
 .../cassandra/cql3/functions/AggregateFcts.java | 11 ++++++
 .../selection/AbstractFunctionSelector.java     |  6 +++
 .../cassandra/cql3/selection/Selector.java      |  1 -
 .../validation/operations/AggregationTest.java  | 39 ++++++++++++++++++++
 6 files changed, 59 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/4fc58513/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index 37a1b9e..a9cf70d 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -100,6 +100,7 @@ New features
    - The toTimestamp(date) and toUnixTimestamp(date) functions have been added 
to allow
      to convert from date into timestamp type and bigint raw value.
    - SizeTieredCompactionStrategy parameter cold_reads_to_omit has been 
removed.
+   - COUNT(*) and COUNT(1) can be selected with other columns or functions
 
 
 2.1.9

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4fc58513/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g 
b/src/java/org/apache/cassandra/cql3/Cql.g
index 0db09b8..3d2aba5 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -295,8 +295,7 @@ selectStatement returns [SelectStatement.RawStatement expr]
     }
     : K_SELECT 
       ( K_JSON { isJson = true; } )?
-      ( ( K_DISTINCT { isDistinct = true; } )? sclause=selectClause
-        | sclause=selectCountClause )
+      ( ( K_DISTINCT { isDistinct = true; } )? sclause=selectClause )
       K_FROM cf=columnFamilyName
       ( K_WHERE wclause=whereClause )?
       ( K_ORDER K_BY orderByClause[orderings] ( ',' orderByClause[orderings] 
)* )?
@@ -324,6 +323,7 @@ selector returns [RawSelector s]
 unaliasedSelector returns [Selectable.Raw s]
     @init { Selectable.Raw tmp = null; }
     :  ( c=cident                                  { tmp = c; }
+       | K_COUNT '(' countArgument ')'             { tmp = new 
Selectable.WithFunction.Raw(FunctionName.nativeFunction("countRows"), 
Collections.<Selectable.Raw>emptyList());}
        | K_WRITETIME '(' c=cident ')'              { tmp = new 
Selectable.WritetimeOrTTL.Raw(c, true); }
        | K_TTL       '(' c=cident ')'              { tmp = new 
Selectable.WritetimeOrTTL.Raw(c, false); }
        | f=functionName args=selectionFunctionArgs { tmp = new 
Selectable.WithFunction.Raw(f, args); }
@@ -337,11 +337,6 @@ selectionFunctionArgs returns [List<Selectable.Raw> a]
       ')' { $a = args; }
     ;
 
-selectCountClause returns [List<RawSelector> expr]
-    @init{ ColumnIdentifier alias = new ColumnIdentifier("count", false); }
-    : K_COUNT '(' countArgument ')' (K_AS c=ident { alias = c; })? { $expr = 
new ArrayList<RawSelector>(); $expr.add( new RawSelector(new 
Selectable.WithFunction.Raw(FunctionName.nativeFunction("countRows"), 
Collections.<Selectable.Raw>emptyList()), alias));}
-    ;
-
 countArgument
     : '\*'
     | i=INTEGER { if (!i.getText().equals("1")) addRecognitionError("Only 
COUNT(1) is supported, got COUNT(" + i.getText() + ")");}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4fc58513/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java 
b/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java
index 1b22da6..41e43c0 100644
--- a/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java
+++ b/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java
@@ -38,6 +38,17 @@ import org.apache.cassandra.db.marshal.ShortType;
 public abstract class AggregateFcts
 {
     /**
+     * Checks if the specified function is the count rows (e.g. COUNT(*) or 
COUNT(1)) function.
+     *
+     * @param function the function to check
+     * @return <code>true</code> if the specified function is the count rows 
one, <code>false</code> otherwise.
+     */
+    public static boolean isCountRows(Function function)
+    {
+        return function == countRowsFunction;
+    }
+
+    /**
      * The function used to count the number of rows of a result set. This 
function is called when COUNT(*) or COUNT(1)
      * is specified.
      */

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4fc58513/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java 
b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
index abf52e1..bf1234f 100644
--- a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
+++ b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java
@@ -22,8 +22,11 @@ import java.util.Arrays;
 import java.util.List;
 
 import com.google.common.collect.Iterables;
+
 import org.apache.commons.lang3.text.StrBuilder;
 
+import org.apache.cassandra.cql3.functions.AggregateFcts;
+
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.cql3.ColumnSpecification;
 import org.apache.cassandra.cql3.functions.Function;
@@ -53,6 +56,9 @@ abstract class AbstractFunctionSelector<T extends Function> 
extends Selector
         {
             protected String getColumnName()
             {
+                if (AggregateFcts.isCountRows(fun))
+                    return "count";
+
                 return new StrBuilder(fun.name().toString()).append('(')
                                                             
.appendWithSeparators(factories.getColumnNames(), ", ")
                                                             .append(')')

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4fc58513/src/java/org/apache/cassandra/cql3/selection/Selector.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/Selector.java 
b/src/java/org/apache/cassandra/cql3/selection/Selector.java
index 9b7f0ba..d53fba1 100644
--- a/src/java/org/apache/cassandra/cql3/selection/Selector.java
+++ b/src/java/org/apache/cassandra/cql3/selection/Selector.java
@@ -21,7 +21,6 @@ import java.nio.ByteBuffer;
 import java.util.Collections;
 
 import org.apache.cassandra.config.CFMetaData;
-import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.cql3.AssignmentTestable;
 import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.ColumnSpecification;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4fc58513/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java
----------------------------------------------------------------------
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java
 
b/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java
index 9881d73..b44fc71 100644
--- 
a/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java
@@ -93,6 +93,45 @@ public class AggregationTest extends CQLTester
     }
 
     @Test
+    public void testCountStarFunction() throws Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, c double, primary key (a, 
b))");
+
+        // Test with empty table
+        assertColumnNames(execute("SELECT COUNT(*) FROM %s"), "count");
+        assertRows(execute("SELECT COUNT(*) FROM %s"), row(0L));
+        assertColumnNames(execute("SELECT COUNT(1) FROM %s"), "count");
+        assertRows(execute("SELECT COUNT(1) FROM %s"), row(0L));
+        assertColumnNames(execute("SELECT COUNT(*), COUNT(*) FROM %s"), 
"count", "count");
+        assertRows(execute("SELECT COUNT(*), COUNT(*) FROM %s"), row(0L, 0L));
+
+        // Test with alias
+        assertColumnNames(execute("SELECT COUNT(*) as myCount FROM %s"), 
"mycount");
+        assertRows(execute("SELECT COUNT(*) as myCount FROM %s"), row(0L));
+        assertColumnNames(execute("SELECT COUNT(1) as myCount FROM %s"), 
"mycount");
+        assertRows(execute("SELECT COUNT(1) as myCount FROM %s"), row(0L));
+
+        // Test invalid call
+        assertInvalidSyntaxMessage("Only COUNT(1) is supported, got COUNT(2)", 
"SELECT COUNT(2) FROM %s");
+
+        // Test with other aggregates
+        assertColumnNames(execute("SELECT COUNT(*), max(b), b FROM %s"), 
"count", "system.max(b)", "b");
+        assertRows(execute("SELECT COUNT(*), max(b), b  FROM %s"), row(0L, 
null, null));
+        assertColumnNames(execute("SELECT COUNT(1), max(b), b FROM %s"), 
"count", "system.max(b)", "b");
+        assertRows(execute("SELECT COUNT(1), max(b), b  FROM %s"), row(0L, 
null, null));
+
+        execute("INSERT INTO %s (a, b, c) VALUES (1, 1, 11.5)");
+        execute("INSERT INTO %s (a, b, c) VALUES (1, 2, 9.5)");
+        execute("INSERT INTO %s (a, b, c) VALUES (1, 3, 9.0)");
+        execute("INSERT INTO %s (a, b, c) VALUES (1, 5, 1.0)");
+
+        assertRows(execute("SELECT COUNT(*) FROM %s"), row(4L));
+        assertRows(execute("SELECT COUNT(1) FROM %s"), row(4L));
+        assertRows(execute("SELECT max(b), b, COUNT(*) FROM %s"), row(5, 1, 
4L));
+        assertRows(execute("SELECT max(b), COUNT(1), b FROM %s"), row(5, 4L, 
1));
+    }
+
+    @Test
     public void testAggregateWithColumns() throws Throwable
     {
         createTable("CREATE TABLE %s (a int, b int, c int, primary key (a, 
b))");

Reply via email to