diff --git a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlLexer.g4 b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlLexer.g4 index f363658b5e..63b0ec0b7e 100644 --- a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlLexer.g4 +++ b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlLexer.g4 @@ -157,6 +157,7 @@ COLLATE : [cC] [oO] [lL] [lL] [aA] [tT] [eE]; CONCAT : [cC] [oO] [nN] [cC] [aA] [tT]; COUNT : [cC] [oO] [uU] [nN] [tT]; CROSS : [cC] [rR] [oO] [sS] [sS]; +CUBE : [cC] [uU] [bB] [eE]; CURRENT : [cC] [uU] [rR] [rR] [eE] [nN] [tT]; CURRENT_DATE : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [dD] [aA] [tT] [eE]; CURRENT_INSTANT : [cC] [uU] [rR] [rR] [eE] [nN] [tT] '_' [iI] [nN] [sS] [tT] [aA] [nN] [tT]; //deprecated legacy @@ -246,6 +247,7 @@ POWER : [pP] [oO] [wW] [eE] [rR]; QUARTER : [qQ] [uU] [aA] [rR] [tT] [eE] [rR]; REPLACE : [rR] [eE] [pP] [lL] [aA] [cC] [eE]; RIGHT : [rR] [iI] [gG] [hH] [tT]; +ROLLUP : [rR] [oO] [lL] [lL] [uU] [pP]; ROUND : [rR] [oO] [uU] [nN] [dD]; SECOND : [sS] [eE] [cC] [oO] [nN] [dD]; SELECT : [sS] [eE] [lL] [eE] [cC] [tT]; diff --git a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 index 51907427a5..1850b3dd58 100644 --- a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 +++ b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 @@ -723,6 +723,8 @@ standardFunction | localTimeFunction | localDateTimeFunction | offsetDateTimeFunction + | cube + | rollup ; @@ -1041,6 +1043,14 @@ positionFunctionStringArgument : expression ; +cube + : CUBE LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN + ; + +rollup + : ROLLUP LEFT_PAREN expression (COMMA expression)* RIGHT_PAREN + ; + /** * The `identifier` is used to provide "keyword as identifier" handling. * diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index d8bd6744c6..ab7b39eb1a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -341,6 +341,10 @@ public abstract class Dialect implements ConversionContext { CommonFunctionFactory.aggregates(queryEngine); + //grouping functions cube() and rollup() supported on some databases + + CommonFunctionFactory.groupings(queryEngine); + //the ANSI SQL-defined aggregate functions any() and every() are only //supported on one database, but can be emulated using sum() and case, //though there is a more natural mapping on some databases diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java index 4519d15154..f43cde5ce2 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java @@ -1419,6 +1419,15 @@ public class CommonFunctionFactory { .register(); } + public static void groupings(QueryEngine queryEngine) { + queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("cube") + .setMinArgumentCount(1) + .register(); + queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("rollup") + .setMinArgumentCount(1) + .register(); + } + public static void aggregates(QueryEngine queryEngine) { queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("max") .setExactArgumentCount(1) diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index d8a95bbe57..8a66658d38 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -3002,6 +3002,36 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre ); } + @Override + public SqmExpression visitCube(HqlParser.CubeContext ctx) { + List> args = new ArrayList<>(); + for ( HqlParser.ExpressionContext arg: ctx.expression() ) { + args.add( (SqmExpression) arg.accept( this ) ); + } + //ignore DISTINCT + return getFunctionDescriptor("cube").generateSqmExpression( + args, + resolveExpressableTypeBasic( Integer.class ), + creationContext.getQueryEngine(), + creationContext.getJpaMetamodel().getTypeConfiguration() + ); + } + + @Override + public SqmExpression visitRollup(HqlParser.RollupContext ctx) { + List> args = new ArrayList<>(); + for ( HqlParser.ExpressionContext arg: ctx.expression() ) { + args.add( (SqmExpression) arg.accept( this ) ); + } + //ignore DISTINCT + return getFunctionDescriptor("rollup").generateSqmExpression( + args, + resolveExpressableTypeBasic( Integer.class ), + creationContext.getQueryEngine(), + creationContext.getJpaMetamodel().getTypeConfiguration() + ); + } + @Override public SqmExpression visitSubstringFunction(HqlParser.SubstringFunctionContext ctx) { final SqmExpression source = (SqmExpression) ctx.expression().accept( this ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index 1b72b2ad5e..9f87f27f9f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -954,4 +954,18 @@ public class FunctionTests extends SessionFactoryBasedFunctionalTest { ); } + @Test + public void testGroupingFunctions() { + inTransaction( + session -> { + session.createQuery("select max(e.theDouble), e.gender, e.theInt from EntityOfBasics e group by e.gender, e.theInt") + .list(); + session.createQuery("select avg(e.theDouble), e.gender, e.theInt from EntityOfBasics e group by rollup(e.gender, e.theInt)") + .list(); + session.createQuery("select sum(e.theDouble), e.gender, e.theInt from EntityOfBasics e group by cube(e.gender, e.theInt)") + .list(); + } + ); + } + } \ No newline at end of file