CAY-2499 Support for COUNT(DISTINCT(column)) function aggregate
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/54b3f811 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/54b3f811 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/54b3f811 Branch: refs/heads/master Commit: 54b3f81107f16ed90d9b24277b19a69114094ae9 Parents: 55c91f9 Author: John Huss <johnth...@apache.org> Authored: Fri Dec 7 14:18:45 2018 -0600 Committer: John Huss <johnth...@apache.org> Committed: Wed Dec 12 14:48:55 2018 -0600 ---------------------------------------------------------------------- RELEASE-NOTES.txt | 3 +- .../cayenne/exp/FunctionExpressionFactory.java | 8 + .../java/org/apache/cayenne/exp/Property.java | 7 + .../apache/cayenne/exp/parser/ASTDistinct.java | 41 + .../cayenne/exp/parser/ExpressionParser.java | 81 +- .../exp/parser/ExpressionParserConstants.java | 83 +- .../parser/ExpressionParserTokenManager.java | 1017 +++++++++--------- .../parser/ExpressionParserTreeConstants.java | 31 +- .../exp/parser/JJTExpressionParserState.java | 19 - .../cayenne/exp/parser/ExpressionParser.jjt | 6 + .../org/apache/cayenne/exp/PropertyTest.java | 10 + .../cayenne/query/ObjectSelect_AggregateIT.java | 16 + 12 files changed, 711 insertions(+), 611 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/54b3f811/RELEASE-NOTES.txt ---------------------------------------------------------------------- diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 90ec498..a885141 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -27,6 +27,7 @@ CAY-2489 Add validation to the case of not to PK relationships CAY-2491 Remaster Db Import View CAY-2493 Save cgen configuration with datamap XML CAY-2494 Rename dbImport tag from 'config' to 'dbImport' +CAY-2499 Support for COUNT(DISTINCT(column)) function aggregate Bug Fixes: @@ -568,4 +569,4 @@ CAY-1804 Serialisation of long[] type was not working correctly. CAY-1806 Error importing eomodel CAY-1817 NPE during Validate Project CAY-1827 EhCache region corresponding to a cache group loses its settings after 'removeGroup' -CAY-1832 Exception when modifying objects in postLoad callback \ No newline at end of file +CAY-1832 Exception when modifying objects in postLoad callback http://git-wip-us.apache.org/repos/asf/cayenne/blob/54b3f811/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java index 114fb1d..7c3c980 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/FunctionExpressionFactory.java @@ -26,6 +26,7 @@ import org.apache.cayenne.exp.parser.ASTCount; import org.apache.cayenne.exp.parser.ASTCurrentDate; import org.apache.cayenne.exp.parser.ASTCurrentTime; import org.apache.cayenne.exp.parser.ASTCurrentTimestamp; +import org.apache.cayenne.exp.parser.ASTDistinct; import org.apache.cayenne.exp.parser.ASTExtract; import org.apache.cayenne.exp.parser.ASTLength; import org.apache.cayenne.exp.parser.ASTLocate; @@ -322,6 +323,13 @@ public class FunctionExpressionFactory { public static Expression countExp(Expression exp) { return new ASTCount(exp); } + + /** + * @return Expression COUNT(DISTINCT(exp)) + */ + public static Expression countDistinctExp(Expression exp) { + return new ASTCount(new ASTDistinct(exp)); + } /** * @return Expression MIN(exp) http://git-wip-us.apache.org/repos/asf/cayenne/blob/54b3f811/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java index 6872615..4df67b1 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Property.java @@ -643,6 +643,13 @@ public class Property<E> { public Property<Long> count() { return create(FunctionExpressionFactory.countExp(getExpression()), Long.class); } + + /** + * @see FunctionExpressionFactory#countDistinctExp(Expression) + */ + public Property<Long> countDistinct() { + return create(FunctionExpressionFactory.countDistinctExp(getExpression()), Long.class); + } /** * @see FunctionExpressionFactory#maxExp(Expression) http://git-wip-us.apache.org/repos/asf/cayenne/blob/54b3f811/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDistinct.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDistinct.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDistinct.java new file mode 100644 index 0000000..39088c3 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDistinct.java @@ -0,0 +1,41 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + +package org.apache.cayenne.exp.parser; + +import org.apache.cayenne.exp.Expression; + +/** + * @since 4.0 + */ +public class ASTDistinct extends ASTAggregateFunctionCall { + + ASTDistinct(int id) { + super(id, "DISTINCT"); + } + + public ASTDistinct(Expression expression) { + super(ExpressionParserTreeConstants.JJTDISTINCT, "DISTINCT", expression); + } + + @Override + public Expression shallowCopy() { + return new ASTDistinct(id); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/54b3f811/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java index 06d4c0d..cbab144 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java @@ -21,6 +21,8 @@ package org.apache.cayenne.exp.parser; +import java.math.BigDecimal; +import java.math.BigInteger; import org.apache.cayenne.exp.Expression; /** @@ -194,10 +196,10 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case HOUR: case MINUTE: case SECOND: - case 64: case 65: case 66: case 67: + case 68: case PROPERTY_PATH: case SINGLE_QUOTED_STRING: case DOUBLE_QUOTED_STRING: @@ -271,10 +273,10 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case HOUR: case MINUTE: case SECOND: - case 64: case 65: case 66: case 67: + case 68: case PROPERTY_PATH: case SINGLE_QUOTED_STRING: case DOUBLE_QUOTED_STRING: @@ -544,7 +546,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon jjtree.openNodeScope(jjtn011); try { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 64: + case 65: namedParameter(); break; case 16: @@ -703,7 +705,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon jjtree.openNodeScope(jjtn003); try { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 64: + case 65: namedParameter(); break; case 16: @@ -833,10 +835,10 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case HOUR: case MINUTE: case SECOND: - case 64: case 65: case 66: case 67: + case 68: case PROPERTY_PATH: case INT_LITERAL: case FLOAT_LITERAL: @@ -884,9 +886,9 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon final public void stringParameter() throws ParseException { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 65: case 66: case 67: + case 68: case PROPERTY_PATH: pathExpression(); break; @@ -987,10 +989,10 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case HOUR: case MINUTE: case SECOND: - case 64: case 65: case 66: case 67: + case 68: case PROPERTY_PATH: case SINGLE_QUOTED_STRING: case DOUBLE_QUOTED_STRING: @@ -1067,7 +1069,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 64: + case 65: namedParameter(); break; case INT_LITERAL: @@ -1509,10 +1511,10 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case HOUR: case MINUTE: case SECOND: - case 64: case 65: case 66: case 67: + case 68: case PROPERTY_PATH: case INT_LITERAL: case FLOAT_LITERAL: @@ -1571,10 +1573,10 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case HOUR: case MINUTE: case SECOND: - case 64: case 65: case 66: case 67: + case 68: case PROPERTY_PATH: case INT_LITERAL: case FLOAT_LITERAL: @@ -1659,7 +1661,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 64: + case 65: namedParameter(); break; case LENGTH: @@ -1679,9 +1681,9 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case SECOND: functionsReturningNumerics(); break; - case 65: case 66: case 67: + case 68: case PROPERTY_PATH: pathExpression(); break; @@ -2149,9 +2151,9 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon case ASTERISK: asterisk(); break; - case 65: case 66: case 67: + case 68: case PROPERTY_PATH: pathExpression(); break; @@ -2440,9 +2442,40 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } + final public void distinct() throws ParseException { + /*@bgen(jjtree) Distinct */ + ASTDistinct jjtn000 = new ASTDistinct(JJTDISTINCT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(DISTINCT); + jj_consume_token(16); + pathExpression(); + jj_consume_token(17); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + final public void namedParameter() throws ParseException { Token t; - jj_consume_token(64); + jj_consume_token(65); t = jj_consume_token(PROPERTY_PATH); ASTNamedParameter jjtn001 = new ASTNamedParameter(JJTNAMEDPARAMETER); boolean jjtc001 = true; @@ -2476,8 +2509,8 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 65: - jj_consume_token(65); + case 66: + jj_consume_token(66); t = jj_consume_token(PROPERTY_PATH); ASTObjPath jjtn002 = new ASTObjPath(JJTOBJPATH); boolean jjtc002 = true; @@ -2492,8 +2525,8 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 66: - jj_consume_token(66); + case 67: + jj_consume_token(67); t = jj_consume_token(PROPERTY_PATH); ASTDbPath jjtn003 = new ASTDbPath(JJTDBPATH); boolean jjtc003 = true; @@ -2508,8 +2541,8 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } break; - case 67: - jj_consume_token(67); + case 68: + jj_consume_token(68); t = jj_consume_token(PROPERTY_PATH); ASTEnum jjtn004 = new ASTEnum(JJTENUM); boolean jjtc004 = true; @@ -2553,10 +2586,10 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon jj_la1_0 = new int[] {0x2,0x4,0x18,0x16010018,0x60,0x180,0x10000,0x4fff8,0x4fff8,0x16010000,0x18,0x10000,0x4e000,0x80000,0x16010000,0x0,0x0,0x16010000,0x0,0x100000,0x200000,0x400000,0x1800000,0x1800000,0x6000000,0x6000000,0x8000000,0x8000000,0x16010000,0x2000000,0x6010000,0x10000,0x0,0x80000,0x80000,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,}; } private static void jj_la1_init_1() { - jj_la1_1 = new int[] {0x0,0x0,0x0,0xfffffffe,0x0,0x0,0x0,0x0,0x0,0xfffffffe,0x0,0x0,0x0,0x0,0xfffffff2,0x3e00,0x3e00,0xfffffffe,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc7c000,0x0,0xffc7c000,0xffc7c000,0x3e00,0x0,0x0,0xffc7c000,0x0,0x1f0,0x0,0x380000,0xffc00000,0x0,}; + jj_la1_1 = new int[] {0x0,0x0,0x0,0xfffffdfe,0x0,0x0,0x0,0x0,0x0,0xfffffdfe,0x0,0x0,0x0,0x0,0xfffffdf2,0x7c00,0x7c00,0xfffffdfe,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff8f8000,0x0,0xff8f8000,0xff8f8000,0x7c00,0x0,0x0,0xff8f8000,0x0,0x1f0,0x0,0x700000,0xff800000,0x0,}; } private static void jj_la1_init_2() { - jj_la1_2 = new int[] {0x0,0x0,0x0,0x7202f,0x0,0x0,0x1,0x0,0x0,0x7202f,0x0,0x1,0x0,0x0,0x7202f,0x1202e,0x12000,0x7202f,0x72001,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x10,0x6002f,0x0,0x6002f,0x6002f,0x0,0x0,0x0,0x0,0x0,0x0,0x3e,0x0,0x0,0x2e,}; + jj_la1_2 = new int[] {0x0,0x0,0x0,0xe405f,0x0,0x0,0x2,0x0,0x0,0xe405f,0x0,0x2,0x0,0x0,0xe405f,0x2405c,0x24000,0xe405f,0xe4002,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x20,0xc005f,0x0,0xc005f,0xc005f,0x0,0x0,0x0,0x1,0x0,0x0,0x7c,0x0,0x1,0x5c,}; } /** Constructor with InputStream. */ @@ -2676,7 +2709,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon /** Generate ParseException. */ public ParseException generateParseException() { jj_expentries.clear(); - boolean[] la1tokens = new boolean[87]; + boolean[] la1tokens = new boolean[88]; if (jj_kind >= 0) { la1tokens[jj_kind] = true; jj_kind = -1; @@ -2696,7 +2729,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon } } } - for (int i = 0; i < 87; i++) { + for (int i = 0; i < 88; i++) { if (la1tokens[i]) { jj_expentry = new int[1]; jj_expentry[0] = i; http://git-wip-us.apache.org/repos/asf/cayenne/blob/54b3f811/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java index 18c00e2..c934325 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java @@ -47,81 +47,83 @@ public interface ExpressionParserConstants { /** RegularExpression Id. */ int COUNT = 40; /** RegularExpression Id. */ - int CONCAT = 41; + int DISTINCT = 41; /** RegularExpression Id. */ - int SUBSTRING = 42; + int CONCAT = 42; /** RegularExpression Id. */ - int TRIM = 43; + int SUBSTRING = 43; /** RegularExpression Id. */ - int LOWER = 44; + int TRIM = 44; /** RegularExpression Id. */ - int UPPER = 45; + int LOWER = 45; /** RegularExpression Id. */ - int LENGTH = 46; + int UPPER = 46; /** RegularExpression Id. */ - int LOCATE = 47; + int LENGTH = 47; /** RegularExpression Id. */ - int ABS = 48; + int LOCATE = 48; /** RegularExpression Id. */ - int SQRT = 49; + int ABS = 49; /** RegularExpression Id. */ - int MOD = 50; + int SQRT = 50; /** RegularExpression Id. */ - int CURRENT_DATE = 51; + int MOD = 51; /** RegularExpression Id. */ - int CURRENT_TIME = 52; + int CURRENT_DATE = 52; /** RegularExpression Id. */ - int CURRENT_TIMESTAMP = 53; + int CURRENT_TIME = 53; /** RegularExpression Id. */ - int YEAR = 54; + int CURRENT_TIMESTAMP = 54; /** RegularExpression Id. */ - int MONTH = 55; + int YEAR = 55; /** RegularExpression Id. */ - int WEEK = 56; + int MONTH = 56; /** RegularExpression Id. */ - int DAY_OF_YEAR = 57; + int WEEK = 57; /** RegularExpression Id. */ - int DAY = 58; + int DAY_OF_YEAR = 58; /** RegularExpression Id. */ - int DAY_OF_MONTH = 59; + int DAY = 59; /** RegularExpression Id. */ - int DAY_OF_WEEK = 60; + int DAY_OF_MONTH = 60; /** RegularExpression Id. */ - int HOUR = 61; + int DAY_OF_WEEK = 61; /** RegularExpression Id. */ - int MINUTE = 62; + int HOUR = 62; /** RegularExpression Id. */ - int SECOND = 63; + int MINUTE = 63; /** RegularExpression Id. */ - int ASTERISK = 68; + int SECOND = 64; /** RegularExpression Id. */ - int PROPERTY_PATH = 69; + int ASTERISK = 69; /** RegularExpression Id. */ - int IDENTIFIER = 70; + int PROPERTY_PATH = 70; /** RegularExpression Id. */ - int LETTER = 71; + int IDENTIFIER = 71; /** RegularExpression Id. */ - int DIGIT = 72; + int LETTER = 72; /** RegularExpression Id. */ - int ESC = 75; + int DIGIT = 73; /** RegularExpression Id. */ - int SINGLE_QUOTED_STRING = 77; + int ESC = 76; /** RegularExpression Id. */ - int STRING_ESC = 78; + int SINGLE_QUOTED_STRING = 78; /** RegularExpression Id. */ - int DOUBLE_QUOTED_STRING = 80; + int STRING_ESC = 79; /** RegularExpression Id. */ - int INT_LITERAL = 81; + int DOUBLE_QUOTED_STRING = 81; /** RegularExpression Id. */ - int FLOAT_LITERAL = 82; + int INT_LITERAL = 82; /** RegularExpression Id. */ - int DEC_FLT = 83; + int FLOAT_LITERAL = 83; /** RegularExpression Id. */ - int DEC_DIGITS = 84; + int DEC_FLT = 84; /** RegularExpression Id. */ - int EXPONENT = 85; + int DEC_DIGITS = 85; /** RegularExpression Id. */ - int FLT_SUFF = 86; + int EXPONENT = 86; + /** RegularExpression Id. */ + int FLT_SUFF = 87; /** Lexical state. */ int DEFAULT = 0; @@ -173,6 +175,7 @@ public interface ExpressionParserConstants { "\"max\"", "\"sum\"", "\"count\"", + "\"distinct\"", "\"concat\"", "\"substring\"", "\"trim\"", @@ -208,10 +211,10 @@ public interface ExpressionParserConstants { "\"\\\'\"", "\"\\\"\"", "<ESC>", - "<token of kind 76>", + "<token of kind 77>", "\"\\\'\"", "<STRING_ESC>", - "<token of kind 79>", + "<token of kind 80>", "\"\\\"\"", "<INT_LITERAL>", "<FLOAT_LITERAL>",