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))");