Add rollup() and cube() for group by clause

This syntax is supported on at least DB2, Oracle, SQL Server, and
Postgres. It's not supported on MySQL.
This commit is contained in:
gavinking 2020-04-22 15:02:04 +02:00 committed by Steve Ebersole
parent 5b614ab454
commit 01d3485970
6 changed files with 69 additions and 0 deletions

View File

@ -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];

View File

@ -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.
*

View File

@ -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

View File

@ -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)

View File

@ -3002,6 +3002,36 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre
);
}
@Override
public SqmExpression visitCube(HqlParser.CubeContext ctx) {
List<SqmTypedNode<?>> 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<SqmTypedNode<?>> 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 );

View File

@ -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();
}
);
}
}