HHH-16715 clean up the code surrounding this stuff

This commit is contained in:
Gavin 2023-05-29 22:55:22 +02:00 committed by Gavin King
parent 3a40f9c829
commit 6c36e98b1a
5 changed files with 178 additions and 82 deletions

View File

@ -215,6 +215,7 @@ INTO : [iI] [nN] [tT] [oO];
IS : [iI] [sS]; IS : [iI] [sS];
JOIN : [jJ] [oO] [iI] [nN]; JOIN : [jJ] [oO] [iI] [nN];
KEY : [kK] [eE] [yY]; KEY : [kK] [eE] [yY];
KEYS : [kK] [eE] [yY] [sS];
LAST : [lL] [aA] [sS] [tT]; LAST : [lL] [aA] [sS] [tT];
LATERAL : [lL] [aA] [tT] [eE] [rR] [aA] [lL]; LATERAL : [lL] [aA] [tT] [eE] [rR] [aA] [lL];
LEADING : [lL] [eE] [aA] [dD] [iI] [nN] [gG]; LEADING : [lL] [eE] [aA] [dD] [iI] [nN] [gG];

View File

@ -450,14 +450,14 @@ treatedNavigablePath
* A 'value()' function that "breaks" a path expression * A 'value()' function that "breaks" a path expression
*/ */
collectionValueNavigablePath collectionValueNavigablePath
: (VALUE|ELEMENT) LEFT_PAREN path RIGHT_PAREN pathContinuation? : elementValueQuantifier LEFT_PAREN path RIGHT_PAREN pathContinuation?
; ;
/** /**
* A 'key()' or 'index()' function that "breaks" a path expression * A 'key()' or 'index()' function that "breaks" a path expression
*/ */
mapKeyNavigablePath mapKeyNavigablePath
: (KEY|INDEX) LEFT_PAREN path RIGHT_PAREN pathContinuation? : indexKeyQuantifier LEFT_PAREN path RIGHT_PAREN pathContinuation?
; ;
@ -626,7 +626,7 @@ predicate
| expression NOT? BETWEEN expression AND expression # BetweenPredicate | expression NOT? BETWEEN expression AND expression # BetweenPredicate
| expression NOT? (LIKE | ILIKE) expression likeEscape? # LikePredicate | expression NOT? (LIKE | ILIKE) expression likeEscape? # LikePredicate
| expression comparisonOperator expression # ComparisonPredicate | expression comparisonOperator expression # ComparisonPredicate
| EXISTS (ELEMENTS|INDICES) LEFT_PAREN simplePath RIGHT_PAREN # ExistsCollectionPartPredicate | EXISTS collectionQuantifier LEFT_PAREN simplePath RIGHT_PAREN # ExistsCollectionPartPredicate
| EXISTS expression # ExistsPredicate | EXISTS expression # ExistsPredicate
| expression NOT? MEMBER OF? path # MemberOfPredicate | expression NOT? MEMBER OF? path # MemberOfPredicate
| NOT predicate # NegatedPredicate | NOT predicate # NegatedPredicate
@ -655,7 +655,7 @@ comparisonOperator
* A list of values, a parameter (for a parameterized list of values), a subquery, or an 'elements()' or 'indices()' function * A list of values, a parameter (for a parameterized list of values), a subquery, or an 'elements()' or 'indices()' function
*/ */
inList inList
: (ELEMENTS|INDICES) LEFT_PAREN simplePath RIGHT_PAREN # PersistentCollectionReferenceInList : collectionQuantifier LEFT_PAREN simplePath RIGHT_PAREN # PersistentCollectionReferenceInList
| LEFT_PAREN (expressionOrPredicate (COMMA expressionOrPredicate)*)? RIGHT_PAREN# ExplicitTupleInList | LEFT_PAREN (expressionOrPredicate (COMMA expressionOrPredicate)*)? RIGHT_PAREN# ExplicitTupleInList
| LEFT_PAREN subquery RIGHT_PAREN # SubqueryInList | LEFT_PAREN subquery RIGHT_PAREN # SubqueryInList
| parameter # ParamInList | parameter # ParamInList
@ -715,6 +715,31 @@ expressionOrPredicate
| predicate | predicate
; ;
collectionQuantifier
: elementsValuesQuantifier
| indicesKeysQuantifier
;
elementValueQuantifier
: ELEMENT
| VALUE
;
indexKeyQuantifier
: INDEX
| KEY
;
elementsValuesQuantifier
: ELEMENTS
| VALUES
;
indicesKeysQuantifier
: INDICES
| KEYS
;
/** /**
* A binary operator with the same precedence as * * A binary operator with the same precedence as *
*/ */
@ -1024,8 +1049,7 @@ function
: standardFunction : standardFunction
| aggregateFunction | aggregateFunction
| collectionSizeFunction | collectionSizeFunction
| indexAggregateFunction | collectionAggregateFunction
| elementAggregateFunction
| collectionFunctionMisuse | collectionFunctionMisuse
| jpaNonstandardFunction | jpaNonstandardFunction
| genericFunction | genericFunction
@ -1079,27 +1103,14 @@ collectionSizeFunction
; ;
/** /**
* The special aggregate collection functions defined by HQL * Special rule for 'max(elements())`, 'avg(keys())', 'sum(indices())`, etc., as defined by HQL
* Also the deprecated 'maxindex()', 'maxelement()', 'minindex()', 'minelement()' functions from old HQL
*/ */
indexAggregateFunction collectionAggregateFunction
: MAXINDEX LEFT_PAREN path RIGHT_PAREN : (MAX|MIN|SUM|AVG) LEFT_PAREN elementsValuesQuantifier LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN # ElementAggregateFunction
| MININDEX LEFT_PAREN path RIGHT_PAREN | (MAX|MIN|SUM|AVG) LEFT_PAREN indicesKeysQuantifier LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN # IndexAggregateFunction
| MAX LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN | (MAXELEMENT|MINELEMENT) LEFT_PAREN path RIGHT_PAREN # ElementAggregateFunction
| MIN LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN | (MAXINDEX|MININDEX) LEFT_PAREN path RIGHT_PAREN # IndexAggregateFunction
| SUM LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
| AVG LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
;
/**
* The special aggregate collection functions defined by HQL
*/
elementAggregateFunction
: MAXELEMENT LEFT_PAREN path RIGHT_PAREN
| MINELEMENT LEFT_PAREN path RIGHT_PAREN
| MAX LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
| MIN LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
| SUM LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
| AVG LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
; ;
/** /**
@ -1109,8 +1120,8 @@ elementAggregateFunction
* and so we have tests that insist they're interchangeable. Ugh.) * and so we have tests that insist they're interchangeable. Ugh.)
*/ */
collectionFunctionMisuse collectionFunctionMisuse
: ELEMENTS LEFT_PAREN path RIGHT_PAREN : elementsValuesQuantifier LEFT_PAREN path RIGHT_PAREN
| INDICES LEFT_PAREN path RIGHT_PAREN | indicesKeysQuantifier LEFT_PAREN path RIGHT_PAREN
; ;
/** /**
@ -1128,18 +1139,28 @@ aggregateFunction
* The functions 'every()' and 'all()' are synonyms * The functions 'every()' and 'all()' are synonyms
*/ */
everyFunction everyFunction
: (EVERY|ALL) LEFT_PAREN predicate RIGHT_PAREN filterClause? overClause? : everyAllQuantifier LEFT_PAREN predicate RIGHT_PAREN filterClause? overClause?
| (EVERY|ALL) LEFT_PAREN subquery RIGHT_PAREN | everyAllQuantifier LEFT_PAREN subquery RIGHT_PAREN
| (EVERY|ALL) (ELEMENTS|INDICES) LEFT_PAREN simplePath RIGHT_PAREN | everyAllQuantifier collectionQuantifier LEFT_PAREN simplePath RIGHT_PAREN
; ;
/** /**
* The functions 'any()' and 'some()' are synonyms * The functions 'any()' and 'some()' are synonyms
*/ */
anyFunction anyFunction
: (ANY|SOME) LEFT_PAREN predicate RIGHT_PAREN filterClause? overClause? : anySomeQuantifier LEFT_PAREN predicate RIGHT_PAREN filterClause? overClause?
| (ANY|SOME) LEFT_PAREN subquery RIGHT_PAREN | anySomeQuantifier LEFT_PAREN subquery RIGHT_PAREN
| (ANY|SOME) (ELEMENTS|INDICES) LEFT_PAREN simplePath RIGHT_PAREN | anySomeQuantifier collectionQuantifier LEFT_PAREN simplePath RIGHT_PAREN
;
everyAllQuantifier
: EVERY
| ALL
;
anySomeQuantifier
: ANY
| SOME
; ;
/** /**
@ -1623,6 +1644,7 @@ rollup
| IS | IS
| JOIN | JOIN
| KEY | KEY
| KEYS
| LAST | LAST
| LATERAL | LATERAL
| LEADING | LEADING

View File

@ -2656,7 +2656,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
try { try {
return new SqmInListPredicate( return new SqmInListPredicate(
testExpression, testExpression,
singletonList( tupleExpressionListContext.getChild( 0 ).accept( this ) ), singletonList( tupleExpressionListContext.parameter().accept( this ) ),
negated, negated,
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
@ -2669,7 +2669,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final HqlParser.SubqueryInListContext subQueryOrParamInListContext = (HqlParser.SubqueryInListContext) inListContext; final HqlParser.SubqueryInListContext subQueryOrParamInListContext = (HqlParser.SubqueryInListContext) inListContext;
return new SqmInSubQueryPredicate( return new SqmInSubQueryPredicate(
testExpression, testExpression,
visitSubquery( (HqlParser.SubqueryContext) subQueryOrParamInListContext.getChild( 1 ) ), visitSubquery( subQueryOrParamInListContext.subquery() ),
negated, negated,
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
@ -2682,8 +2682,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return new SqmInSubQueryPredicate<>( return new SqmInSubQueryPredicate<>(
testExpression, testExpression,
createCollectionReferenceSubQuery( createCollectionReferenceSubQuery(
(HqlParser.SimplePathContext) collectionReferenceInListContext.getChild( 2 ), collectionReferenceInListContext.simplePath(),
(TerminalNode) collectionReferenceInListContext.getChild( 0 ) (TerminalNode) collectionReferenceInListContext.collectionQuantifier().getChild(0).getChild(0)
), ),
negated, negated,
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
@ -2697,7 +2697,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override @Override
public SqmPredicate visitExistsCollectionPartPredicate(HqlParser.ExistsCollectionPartPredicateContext ctx) { public SqmPredicate visitExistsCollectionPartPredicate(HqlParser.ExistsCollectionPartPredicateContext ctx) {
final SqmSubQuery<Object> subQuery = createCollectionReferenceSubQuery( final SqmSubQuery<Object> subQuery = createCollectionReferenceSubQuery(
(HqlParser.SimplePathContext) ctx.getChild( 3 ), ctx.simplePath(),
null null
); );
return new SqmExistsPredicate( subQuery, creationContext.getNodeBuilder() ); return new SqmExistsPredicate( subQuery, creationContext.getNodeBuilder() );
@ -2705,18 +2705,18 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override @Override
public SqmPredicate visitExistsPredicate(HqlParser.ExistsPredicateContext ctx) { public SqmPredicate visitExistsPredicate(HqlParser.ExistsPredicateContext ctx) {
final SqmExpression<?> expression = (SqmExpression<?>) ctx.getChild( 1 ).accept( this ); final SqmExpression<?> expression = (SqmExpression<?>) ctx.expression().accept( this );
return new SqmExistsPredicate( expression, creationContext.getNodeBuilder() ); return new SqmExistsPredicate( expression, creationContext.getNodeBuilder() );
} }
@Override @SuppressWarnings("rawtypes") @Override
public SqmPredicate visitBooleanExpressionPredicate(HqlParser.BooleanExpressionPredicateContext ctx) { public SqmPredicate visitBooleanExpressionPredicate(HqlParser.BooleanExpressionPredicateContext ctx) {
final SqmExpression expression = (SqmExpression) ctx.expression().accept( this ); final SqmExpression<?> expression = (SqmExpression<?>) ctx.expression().accept( this );
if ( expression.getJavaType() != Boolean.class ) { if ( expression.getJavaType() != Boolean.class ) {
throw new SemanticException( "Non-boolean expression used in predicate context: " + ctx.getText() ); throw new SemanticException( "Non-boolean expression used in predicate context: " + ctx.getText() );
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final SqmExpression<Boolean> booleanExpression = expression; final SqmExpression<Boolean> booleanExpression = (SqmExpression<Boolean>) expression;
return new SqmBooleanExpressionPredicate( booleanExpression, creationContext.getNodeBuilder() ); return new SqmBooleanExpressionPredicate( booleanExpression, creationContext.getNodeBuilder() );
} }
@ -4497,7 +4497,10 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
} }
return new SqmEvery<>( return new SqmEvery<>(
createCollectionReferenceSubQuery( ctx.simplePath(), (TerminalNode) ctx.getChild( 1 ) ), createCollectionReferenceSubQuery(
ctx.simplePath(),
(TerminalNode) ctx.collectionQuantifier().getChild(0).getChild(0)
),
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
} }
@ -4530,7 +4533,10 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
} }
return new SqmAny<>( return new SqmAny<>(
createCollectionReferenceSubQuery( ctx.simplePath(), (TerminalNode) ctx.getChild( 1 ) ), createCollectionReferenceSubQuery(
ctx.simplePath(),
(TerminalNode) ctx.collectionQuantifier().getChild(0).getChild(0)
),
creationContext.getNodeBuilder() creationContext.getNodeBuilder()
); );
} }
@ -4910,14 +4916,15 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
// Note: this is a total misuse of the elements() and indices() functions, // Note: this is a total misuse of the elements() and indices() functions,
// which are supposed to be a shortcut way to write a subquery! // which are supposed to be a shortcut way to write a subquery!
// used this way, they're just a worse way to write value()/index() // used this way, they're just a worse way to write value()/index()
log.warn("Misuse of HQL elements() or indices() function, use element() or index() instead");
if ( getCreationOptions().useStrictJpaCompliance() ) { if ( getCreationOptions().useStrictJpaCompliance() ) {
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
} }
final SqmPath<?> pluralAttributePath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) ); final SqmPath<?> pluralAttributePath = consumeDomainPath( ctx.path() );
final SqmPathSource<?> referencedPathSource = pluralAttributePath.getReferencedPathSource(); final SqmPathSource<?> referencedPathSource = pluralAttributePath.getReferencedPathSource();
final TerminalNode firstNode = (TerminalNode) ctx.getChild( 0 ); final TerminalNode firstNode = (TerminalNode) ctx.getChild( 0 ).getChild( 0 );
if ( !(referencedPathSource instanceof PluralPersistentAttribute<?, ?, ?> ) ) { if ( !(referencedPathSource instanceof PluralPersistentAttribute<?, ?, ?> ) ) {
throw new PathException( throw new PathException(
@ -4945,40 +4952,71 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
} }
@Override @Override
public SqmElementAggregateFunction<?> visitElementAggregateFunction(HqlParser.ElementAggregateFunctionContext ctx) { public SqmExpression<?> visitElementAggregateFunction(HqlParser.ElementAggregateFunctionContext ctx) {
if ( creationOptions.useStrictJpaCompliance() ) { if ( creationOptions.useStrictJpaCompliance() ) {
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
} }
SqmPath<?> pluralPath = consumePluralAttributeReference( ctx.path() ); // the actual function name might be 'minelement' or 'maxelement', so trim it
if ( !(pluralPath instanceof SqmPluralValuedSimplePath) ) { final String functionName = ctx.getChild(0).getText().substring(0, 3);
throw new SemanticException( "Path '" + ctx.path().getText() + "' did not resolve to a many-valued attribute" );
}
String functionName = ctx.getChild(0).getText().substring(0, 3); final SqmPath<?> pluralPath = consumePluralAttributeReference( ctx.path() );
if ( pluralPath instanceof SqmPluralValuedSimplePath ) {
return new SqmElementAggregateFunction<>( pluralPath, functionName ); return new SqmElementAggregateFunction<>( pluralPath, functionName );
} }
else {
// elements() and values() and only apply to compound paths
if ( pluralPath instanceof SqmMapJoin ) {
throw new SemanticException( "Path '" + ctx.path().getText()
+ "' resolved to a joined map instead of a compound path" );
}
else if ( pluralPath instanceof SqmListJoin ) {
throw new SemanticException( "Path '" + ctx.path().getText()
+ "' resolved to a joined list instead of a compound path" );
}
else {
throw new SemanticException( "Path '" + ctx.path().getText()
+ "' did not resolve to a many-valued attribute" );
}
}
}
@Override @Override
public SqmIndexAggregateFunction<?> visitIndexAggregateFunction(HqlParser.IndexAggregateFunctionContext ctx) { public SqmExpression<?> visitIndexAggregateFunction(HqlParser.IndexAggregateFunctionContext ctx) {
if ( creationOptions.useStrictJpaCompliance() ) { if ( creationOptions.useStrictJpaCompliance() ) {
throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION ); throw new StrictJpaComplianceViolation( StrictJpaComplianceViolation.Type.HQL_COLLECTION_FUNCTION );
} }
final SqmPath<?> pluralPath = consumePluralAttributeReference( ctx.path() ); // the actual function name might be 'minindex' or 'maxindex', so trim it
if ( !(pluralPath instanceof SqmPluralValuedSimplePath) ) { final String functionName = ctx.getChild(0).getText().substring(0, 3);
throw new SemanticException( "Path '" + ctx.path().getText() + "' did not resolve to a many-valued attribute" );
}
if ( !isIndexedPluralAttribute( pluralPath ) ) {
throw new SemanticException(
"maxindex() function can only be applied to path expressions which resolve to an " +
"indexed collection (list,map); specified path [" + ctx.path() +
"] resolved to " + pluralPath.getReferencedPathSource()
);
}
String functionName = ctx.getChild(0).getText().substring(0, 3); final SqmPath<?> pluralPath = consumePluralAttributeReference( ctx.path() );
return new SqmIndexAggregateFunction<>( pluralPath, functionName ); if ( pluralPath instanceof SqmPluralValuedSimplePath ) {
if ( isIndexedPluralAttribute( pluralPath ) ) {
return new SqmIndexAggregateFunction<>(pluralPath, functionName);
}
else {
throw new SemanticException( "Path '" + ctx.path()
+ "' resolved to '"
+ pluralPath.getReferencedPathSource()
+ "' which is not an indexed collection" );
}
}
else {
// indices() and keys() only apply to compound paths
if ( pluralPath instanceof SqmMapJoin ) {
throw new SemanticException( "Path '" + ctx.path().getText()
+ "' resolved to a joined map instead of a compound path" );
}
else if ( pluralPath instanceof SqmListJoin ) {
throw new SemanticException( "Path '" + ctx.path().getText()
+ "' resolved to a joined list instead of a compound path" );
}
else {
throw new SemanticException( "Path '" + ctx.path().getText()
+ "' did not resolve to a many-valued attribute" );
}
}
} }
@Override @Override
@ -5122,7 +5160,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final boolean hasContinuations = numberOfContinuations != 0; final boolean hasContinuations = numberOfContinuations != 0;
final DotIdentifierConsumer dotIdentifierConsumer = dotIdentifierConsumerStack.getCurrent(); final DotIdentifierConsumer dotIdentifierConsumer = dotIdentifierConsumerStack.getCurrent();
final HqlParser.IdentifierContext identifierContext = (HqlParser.IdentifierContext) ctx.getChild( 0 ); final HqlParser.IdentifierContext identifierContext = ctx.identifier();
assert identifierContext.getChildCount() == 1; assert identifierContext.getChildCount() == 1;
dotIdentifierConsumer.consumeIdentifier( dotIdentifierConsumer.consumeIdentifier(
@ -5134,7 +5172,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( hasContinuations ) { if ( hasContinuations ) {
for ( int i = 1; i < ctx.getChildCount(); i++ ) { for ( int i = 1; i < ctx.getChildCount(); i++ ) {
final HqlParser.SimplePathElementContext continuation = (HqlParser.SimplePathElementContext) ctx.getChild( i ); final HqlParser.SimplePathElementContext continuation = (HqlParser.SimplePathElementContext) ctx.getChild( i );
final HqlParser.IdentifierContext identifier = (HqlParser.IdentifierContext) continuation.getChild( 1 ); final HqlParser.IdentifierContext identifier = continuation.identifier();
assert identifier.getChildCount() == 1; assert identifier.getChildCount() == 1;
dotIdentifierConsumer.consumeIdentifier( dotIdentifierConsumer.consumeIdentifier(
visitIdentifier( identifier ), visitIdentifier( identifier ),
@ -5161,9 +5199,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
else { else {
madeNested = false; madeNested = false;
} }
consumeManagedTypeReference( (HqlParser.PathContext) ctx.getChild( 2 ) ); consumeManagedTypeReference( ctx.path() );
final String treatTargetName = ctx.getChild( 4 ).getText(); final String treatTargetName = ctx.simplePath().getText();
final String treatTargetEntityName = getCreationContext().getJpaMetamodel().qualifyImportableName( treatTargetName ); final String treatTargetEntityName = getCreationContext().getJpaMetamodel().qualifyImportableName( treatTargetName );
final boolean hasContinuation = ctx.getChildCount() == 7; final boolean hasContinuation = ctx.getChildCount() == 7;
@ -5186,7 +5224,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
); );
} }
try { try {
result = consumeDomainPath( (HqlParser.SimplePathContext) ctx.getChild( 6 ).getChild( 1 ) ); result = consumeDomainPath( ctx.pathContinuation().simplePath() );
} }
finally { finally {
if ( addConsumer ) { if ( addConsumer ) {
@ -5212,11 +5250,11 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
else { else {
madeNested = false; madeNested = false;
} }
final SqmPath<?> sqmPath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) ); final SqmPath<?> sqmPath = consumeDomainPath( ctx.path() );
final boolean hasContinuation = ctx.getChildCount() == 5; final boolean hasContinuation = ctx.getChildCount() == 5;
final SqmPathSource<?> referencedPathSource = sqmPath.getReferencedPathSource(); final SqmPathSource<?> referencedPathSource = sqmPath.getReferencedPathSource();
final TerminalNode firstNode = (TerminalNode) ctx.getChild( 0 ); final TerminalNode firstNode = (TerminalNode) ctx.elementValueQuantifier().getChild(0);
checkPluralPath( sqmPath, referencedPathSource, firstNode ); checkPluralPath( sqmPath, referencedPathSource, firstNode );
if ( getCreationOptions().useStrictJpaCompliance() ) { if ( getCreationOptions().useStrictJpaCompliance() ) {
@ -5245,8 +5283,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
// Reset the nested state before consuming the terminal identifier // Reset the nested state before consuming the terminal identifier
( (QualifiedJoinPathConsumer) consumer ).setNested( false ); ( (QualifiedJoinPathConsumer) consumer ).setNested( false );
} }
final HqlParser.SimplePathContext identCtx = (HqlParser.SimplePathContext) ctx.getChild( 4 ) final HqlParser.SimplePathContext identCtx = ctx.pathContinuation().simplePath();
.getChild( 1 );
if ( consumer instanceof QualifiedJoinPathConsumer) { if ( consumer instanceof QualifiedJoinPathConsumer) {
result = consumeDomainPath( identCtx ); result = consumeDomainPath( identCtx );
} }
@ -5285,11 +5322,11 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
else { else {
madeNested = false; madeNested = false;
} }
final SqmPath<?> sqmPath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) ); final SqmPath<?> sqmPath = consumeDomainPath( ctx.path() );
final boolean hasContinuation = ctx.getChildCount() == 5; final boolean hasContinuation = ctx.getChildCount() == 5;
final SqmPathSource<?> referencedPathSource = sqmPath.getReferencedPathSource(); final SqmPathSource<?> referencedPathSource = sqmPath.getReferencedPathSource();
final TerminalNode firstNode = (TerminalNode) ctx.getChild( 0 ); final TerminalNode firstNode = (TerminalNode) ctx.indexKeyQuantifier().getChild(0);
checkPluralPath( sqmPath, referencedPathSource, firstNode ); checkPluralPath( sqmPath, referencedPathSource, firstNode );
if ( getCreationOptions().useStrictJpaCompliance() ) { if ( getCreationOptions().useStrictJpaCompliance() ) {
@ -5333,8 +5370,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
// Reset the nested state before consuming the terminal identifier // Reset the nested state before consuming the terminal identifier
( (QualifiedJoinPathConsumer) consumer ).setNested( false ); ( (QualifiedJoinPathConsumer) consumer ).setNested( false );
} }
final HqlParser.SimplePathContext identCtx = (HqlParser.SimplePathContext) ctx.getChild( 4 ) final HqlParser.SimplePathContext identCtx = ctx.pathContinuation().simplePath();
.getChild( 1 );
if ( consumer instanceof QualifiedJoinPathConsumer) { if ( consumer instanceof QualifiedJoinPathConsumer) {
result = consumeDomainPath( identCtx ); result = consumeDomainPath( identCtx );
} }

View File

@ -106,7 +106,7 @@ public class SqmMapJoin<O, K, V>
} }
@Override @Override
public Path<V> value() { public SqmPath<V> value() {
final SqmPathSource<V> elementPathSource = getReferencedPathSource().getElementPathSource(); final SqmPathSource<V> elementPathSource = getReferencedPathSource().getElementPathSource();
return resolvePath( elementPathSource.getPathName(), elementPathSource ); return resolvePath( elementPathSource.getPathName(), elementPathSource );
} }

View File

@ -294,6 +294,43 @@ public class FunctionTests {
); );
} }
@Test
public void testAggregateIndexElementWithPath(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
assertThat( session.createQuery("select max(index(eol.listOfNumbers)) from EntityOfLists eol", Integer.class)
.getSingleResult(), is(1) );
assertThat( session.createQuery("select max(element(eol.listOfNumbers)) from EntityOfLists eol", Double.class)
.getSingleResult(), is(2.0) );
assertThat( session.createQuery("select sum(index(eol.listOfNumbers)) from EntityOfLists eol", Long.class)
.getSingleResult(), is(1L) );
assertThat( session.createQuery("select sum(element(eol.listOfNumbers)) from EntityOfLists eol", Double.class)
.getSingleResult(), is(3.0) );
assertThat( session.createQuery("select avg(index(eol.listOfNumbers)) from EntityOfLists eol", Double.class)
.getSingleResult(), is(0.5) );
assertThat( session.createQuery("select avg(element(eol.listOfNumbers)) from EntityOfLists eol", Double.class)
.getSingleResult(), is(1.5) );
assertThat( session.createQuery("select max(key(eom.numberByNumber)) from EntityOfMaps eom", Integer.class)
.getSingleResult(), is(1) );
assertThat( session.createQuery("select max(element(eom.numberByNumber)) from EntityOfMaps eom", Double.class)
.getSingleResult(), is(1.0) );
assertThat( session.createQuery("select sum(key(eom.numberByNumber)) from EntityOfMaps eom", Long.class)
.getSingleResult(), is(1L) );
assertThat( session.createQuery("select sum(element(eom.numberByNumber)) from EntityOfMaps eom", Double.class)
.getSingleResult(), is(1.0) );
assertThat( session.createQuery("select avg(key(eom.numberByNumber)) from EntityOfMaps eom", Double.class)
.getSingleResult(), is(1.0) );
assertThat( session.createQuery("select avg(element(eom.numberByNumber)) from EntityOfMaps eom", Double.class)
.getSingleResult(), is(1.0) );
}
);
}
@Test @Test
public void testAggregateIndexElementKeyValueWithAlias(SessionFactoryScope scope) { public void testAggregateIndexElementKeyValueWithAlias(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(