HHH-16715 clean up the code surrounding this stuff
This commit is contained in:
parent
3a40f9c829
commit
6c36e98b1a
|
@ -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];
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue