Skip to content

Commit 899120a

Browse files
committed
HHH-19240 Simplify queryExpression grammar rule
(cherry picked from commit 0e158e0)
1 parent a12b4b7 commit 899120a

File tree

2 files changed

+106
-115
lines changed

2 files changed

+106
-115
lines changed

hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,7 @@ cycleClause
159159
* A toplevel query of subquery, which may be a union or intersection of subqueries
160160
*/
161161
queryExpression
162-
: withClause? orderedQuery # SimpleQueryGroup
163-
| withClause? orderedQuery (setOperator orderedQuery)+ # SetQueryGroup
162+
: withClause? orderedQuery (setOperator orderedQuery)*
164163
;
165164

166165
/**

hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java

Lines changed: 105 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -807,87 +807,14 @@ public Object visitCte(HqlParser.CteContext ctx) {
807807
final JpaCteCriteria<?> oldCte = currentPotentialRecursiveCte;
808808
try {
809809
currentPotentialRecursiveCte = null;
810-
if ( queryExpressionContext instanceof HqlParser.SetQueryGroupContext ) {
811-
final HqlParser.SetQueryGroupContext setContext = (HqlParser.SetQueryGroupContext) queryExpressionContext;
812-
// A recursive query is only possible if the child count is lower than 5 e.g. `withClause? q1 op q2`
813-
if ( setContext.getChildCount() < 5 ) {
814-
final SetOperator setOperator = (SetOperator) setContext.getChild( setContext.getChildCount() - 2 )
815-
.accept( this );
816-
switch ( setOperator ) {
817-
case UNION:
818-
case UNION_ALL:
819-
final HqlParser.OrderedQueryContext nonRecursiveQueryContext;
820-
final HqlParser.OrderedQueryContext recursiveQueryContext;
821-
// On count == 4, we have a withClause at index 0
822-
if ( setContext.getChildCount() == 4 ) {
823-
nonRecursiveQueryContext = (HqlParser.OrderedQueryContext) setContext.getChild( 1 );
824-
recursiveQueryContext = (HqlParser.OrderedQueryContext) setContext.getChild( 3 );
825-
}
826-
else {
827-
nonRecursiveQueryContext = (HqlParser.OrderedQueryContext) setContext.getChild( 0 );
828-
recursiveQueryContext = (HqlParser.OrderedQueryContext) setContext.getChild( 2 );
829-
}
830-
// First visit the non-recursive part
831-
nonRecursiveQueryContext.accept( this );
832-
833-
// Visiting the possibly recursive part must happen within the call to SqmCteContainer.with,
834-
// because in there, the SqmCteStatement/JpaCteCriteria is available for use in the recursive part.
835-
// The structure (SqmCteTable) for the SqmCteStatement is based on the non-recursive part,
836-
// which is necessary to have, so that the SqmCteRoot/SqmCteJoin can resolve sub-paths.
837-
final SqmSelectStatement<Object> recursivePart = new SqmSelectStatement<>( creationContext.getNodeBuilder() );
838-
839-
processingStateStack.pop();
840-
processingStateStack.push(
841-
new SqmQueryPartCreationProcessingStateStandardImpl(
842-
processingStateStack.getCurrent(),
843-
recursivePart,
844-
this
845-
)
846-
);
847-
final JpaCteCriteria<Object> cteDefinition;
848-
if ( setOperator == SetOperator.UNION ) {
849-
cteDefinition = cteContainer.withRecursiveUnionDistinct(
850-
name,
851-
cte,
852-
cteCriteria -> {
853-
currentPotentialRecursiveCte = cteCriteria;
854-
recursiveQueryContext.accept( this );
855-
return recursivePart;
856-
}
857-
);
858-
}
859-
else {
860-
cteDefinition = cteContainer.withRecursiveUnionAll(
861-
name,
862-
cte,
863-
cteCriteria -> {
864-
currentPotentialRecursiveCte = cteCriteria;
865-
recursiveQueryContext.accept( this );
866-
return recursivePart;
867-
}
868-
);
869-
}
870-
if ( materialization != null ) {
871-
cteDefinition.setMaterialization( materialization );
872-
}
873-
final ParseTree lastChild = ctx.getChild( ctx.getChildCount() - 1 );
874-
final ParseTree potentialSearchClause;
875-
if ( lastChild instanceof HqlParser.CycleClauseContext ) {
876-
applyCycleClause( cteDefinition, (HqlParser.CycleClauseContext) lastChild );
877-
potentialSearchClause = ctx.getChild( ctx.getChildCount() - 2 );
878-
}
879-
else {
880-
potentialSearchClause = lastChild;
881-
}
882-
if ( potentialSearchClause instanceof HqlParser.SearchClauseContext ) {
883-
applySearchClause( cteDefinition, (HqlParser.SearchClauseContext) potentialSearchClause );
884-
}
885-
return null;
886-
}
810+
// A recursive query is only possible if there are 2 ordered queries e.g. `q1 op q2`
811+
if ( queryExpressionContext.orderedQuery().size() == 2 ) {
812+
if ( handleRecursive( ctx, queryExpressionContext, cteContainer, name, cte, materialization ) ) {
813+
return null;
887814
}
888815
}
889816
queryExpressionContext.accept( this );
890-
final JpaCteCriteria<Object> cteDefinition = cteContainer.with( name, cte );
817+
final JpaCteCriteria<?> cteDefinition = cteContainer.with( name, cte );
891818
if ( materialization != null ) {
892819
cteDefinition.setMaterialization( materialization );
893820
}
@@ -899,6 +826,76 @@ public Object visitCte(HqlParser.CteContext ctx) {
899826
return null;
900827
}
901828

829+
private boolean handleRecursive(
830+
HqlParser.CteContext cteContext,
831+
HqlParser.QueryExpressionContext setContext,
832+
SqmCteContainer cteContainer,
833+
String name,
834+
SqmSelectQuery<Object> cte,
835+
CteMaterialization materialization) {
836+
final SetOperator setOperator = (SetOperator) setContext.setOperator(0).accept( this );
837+
switch ( setOperator ) {
838+
case UNION:
839+
case UNION_ALL:
840+
final var nonRecursiveQueryContext = setContext.orderedQuery(0);
841+
final var recursiveQueryContext = setContext.orderedQuery(1);
842+
// First visit the non-recursive part
843+
nonRecursiveQueryContext.accept( this );
844+
845+
// Visiting the possibly recursive part must happen within the call to SqmCteContainer.with,
846+
// because in there, the SqmCteStatement/JpaCteCriteria is available for use in the recursive part.
847+
// The structure (SqmCteTable) for the SqmCteStatement is based on the non-recursive part,
848+
// which is necessary to have, so that the SqmCteRoot/SqmCteJoin can resolve sub-paths.
849+
final SqmSelectStatement<Object> recursivePart =
850+
new SqmSelectStatement<>( creationContext.getNodeBuilder() );
851+
852+
processingStateStack.pop();
853+
processingStateStack.push(
854+
new SqmQueryPartCreationProcessingStateStandardImpl(
855+
processingStateStack.getCurrent(),
856+
recursivePart,
857+
this
858+
)
859+
);
860+
final JpaCteCriteria<Object> cteDefinition;
861+
if ( setOperator == SetOperator.UNION ) {
862+
cteDefinition = cteContainer.withRecursiveUnionDistinct(
863+
name,
864+
cte,
865+
cteCriteria -> {
866+
currentPotentialRecursiveCte = cteCriteria;
867+
recursiveQueryContext.accept( this );
868+
return recursivePart;
869+
}
870+
);
871+
}
872+
else {
873+
cteDefinition = cteContainer.withRecursiveUnionAll(
874+
name,
875+
cte,
876+
cteCriteria -> {
877+
currentPotentialRecursiveCte = cteCriteria;
878+
recursiveQueryContext.accept( this );
879+
return recursivePart;
880+
}
881+
);
882+
}
883+
if ( materialization != null ) {
884+
cteDefinition.setMaterialization( materialization );
885+
}
886+
final var cycleClauseContext = cteContext.cycleClause();
887+
if ( cycleClauseContext != null ) {
888+
applyCycleClause( cteDefinition, cycleClauseContext );
889+
}
890+
final var searchClauseContext = cteContext.searchClause();
891+
if ( searchClauseContext != null ) {
892+
applySearchClause( cteDefinition, searchClauseContext );
893+
}
894+
return true;
895+
}
896+
return false;
897+
}
898+
902899
private void applyCycleClause(JpaCteCriteria<?> cteDefinition, HqlParser.CycleClauseContext ctx) {
903900
final HqlParser.CteAttributesContext attributesContext = ctx.cteAttributes();
904901
final String cycleMarkAttributeName = visitIdentifier( (HqlParser.IdentifierContext) ctx.getChild( 3 ) );
@@ -1016,15 +1013,6 @@ private void applySearchClause(JpaCteCriteria<?> cteDefinition, HqlParser.Search
10161013
cteDefinition.search( kind, searchAttributeName, searchOrders );
10171014
}
10181015

1019-
@Override
1020-
public SqmQueryPart<?> visitSimpleQueryGroup(HqlParser.SimpleQueryGroupContext ctx) {
1021-
final int lastChild = ctx.getChildCount() - 1;
1022-
if ( lastChild != 0 ) {
1023-
ctx.getChild( 0 ).accept( this );
1024-
}
1025-
return (SqmQueryPart<?>) ctx.getChild( lastChild ).accept( this );
1026-
}
1027-
10281016
@Override
10291017
public SqmQueryPart<?> visitQueryOrderExpression(HqlParser.QueryOrderExpressionContext ctx) {
10301018
final SqmQuerySpec<?> sqmQuerySpec = currentQuerySpec();
@@ -1064,37 +1052,41 @@ public SqmQueryPart<?> visitNestedQueryExpression(HqlParser.NestedQueryExpressio
10641052
}
10651053

10661054
@Override
1067-
public SqmQueryGroup<?> visitSetQueryGroup(HqlParser.SetQueryGroupContext ctx) {
1068-
final List<ParseTree> children = ctx.children;
1069-
final int firstIndex;
1070-
if ( children.get( 0 ) instanceof HqlParser.WithClauseContext ) {
1071-
children.get( 0 ).accept( this );
1072-
firstIndex = 1;
1073-
}
1074-
else {
1075-
firstIndex = 0;
1076-
}
1055+
public SqmQueryPart<?> visitQueryExpression(HqlParser.QueryExpressionContext ctx) {
10771056
if ( creationOptions.useStrictJpaCompliance() ) {
10781057
throw new StrictJpaComplianceViolation(
10791058
StrictJpaComplianceViolation.Type.SET_OPERATIONS
10801059
);
10811060
}
1082-
final SqmQueryPart<?> firstQueryPart = (SqmQueryPart<?>) children.get( firstIndex ).accept( this );
1061+
var withClauseContext = ctx.withClause();
1062+
if ( withClauseContext != null ) {
1063+
withClauseContext.accept( this );
1064+
}
1065+
final var orderedQueryContexts = ctx.orderedQuery();
1066+
final SqmQueryPart<?> firstQueryPart =
1067+
(SqmQueryPart<?>) orderedQueryContexts.get( 0 ).accept( this );
1068+
if ( orderedQueryContexts.size() == 1 ) {
1069+
return firstQueryPart;
1070+
}
10831071
SqmQueryGroup<?> queryGroup;
1084-
if ( firstQueryPart instanceof SqmQueryGroup<?>) {
1085-
queryGroup = (SqmQueryGroup<?>) firstQueryPart;
1072+
if (firstQueryPart instanceof SqmQueryGroup<?>) {
1073+
queryGroup = (SqmQueryGroup) firstQueryPart;
10861074
}
10871075
else {
10881076
queryGroup = new SqmQueryGroup<>( firstQueryPart );
10891077
}
10901078
setCurrentQueryPart( queryGroup );
1091-
final int size = children.size();
1079+
final var setOperatorContexts = ctx.setOperator();
10921080
final SqmCreationProcessingState firstProcessingState = processingStateStack.pop();
1093-
for ( int i = firstIndex + 1; i < size; i += 2 ) {
1094-
final SetOperator operator = visitSetOperator( (HqlParser.SetOperatorContext) children.get(i) );
1095-
final HqlParser.OrderedQueryContext simpleQueryCtx =
1096-
(HqlParser.OrderedQueryContext) children.get( i + 1 );
1097-
queryGroup = getSqmQueryGroup( operator, simpleQueryCtx, queryGroup, size, firstProcessingState, i );
1081+
for ( int i = 0; i < setOperatorContexts.size(); i++ ) {
1082+
queryGroup = getSqmQueryGroup(
1083+
visitSetOperator( setOperatorContexts.get(i) ),
1084+
orderedQueryContexts.get( i + 1 ),
1085+
queryGroup,
1086+
setOperatorContexts.size(),
1087+
firstProcessingState,
1088+
i
1089+
);
10981090
}
10991091
processingStateStack.push( firstProcessingState );
11001092

@@ -1108,16 +1100,16 @@ private <X> SqmQueryGroup<X> getSqmQueryGroup(
11081100
int size,
11091101
SqmCreationProcessingState firstProcessingState,
11101102
int i) {
1111-
1112-
final List<SqmQueryPart<X>> queryParts;
11131103
processingStateStack.push(
11141104
new SqmQueryPartCreationProcessingStateStandardImpl(
11151105
processingStateStack.getCurrent(),
11161106
firstProcessingState.getProcessingQuery(),
11171107
this
11181108
)
11191109
);
1120-
if ( queryGroup.getSetOperator() == null || queryGroup.getSetOperator() == operator ) {
1110+
final List<SqmQueryPart<X>> queryParts;
1111+
final SetOperator setOperator = queryGroup.getSetOperator();
1112+
if ( setOperator == null || setOperator == operator ) {
11211113
queryGroup.setSetOperator( operator );
11221114
queryParts = queryGroup.queryParts();
11231115
}
@@ -1129,15 +1121,14 @@ private <X> SqmQueryGroup<X> getSqmQueryGroup(
11291121
}
11301122

11311123
try {
1132-
final List<ParseTree> subChildren = simpleQueryCtx.children;
1133-
if ( subChildren.get( 0 ) instanceof HqlParser.QueryContext ) {
1124+
if ( simpleQueryCtx instanceof HqlParser.QuerySpecExpressionContext ) {
11341125
final SqmQuerySpec<X> querySpec = new SqmQuerySpec<>( creationContext.getNodeBuilder() );
11351126
queryParts.add( querySpec );
11361127
visitQuerySpecExpression( (HqlParser.QuerySpecExpressionContext) simpleQueryCtx );
11371128
}
1138-
else {
1129+
else if ( simpleQueryCtx instanceof HqlParser.NestedQueryExpressionContext ) {
11391130
try {
1140-
final SqmSelectStatement<Object> selectStatement =
1131+
final SqmSelectStatement<?> selectStatement =
11411132
new SqmSelectStatement<>( creationContext.getNodeBuilder() );
11421133
processingStateStack.push(
11431134
new SqmQueryPartCreationProcessingStateStandardImpl(
@@ -1155,6 +1146,7 @@ private <X> SqmQueryGroup<X> getSqmQueryGroup(
11551146
processingStateStack.pop();
11561147
}
11571148
}
1149+
// else if QueryOrderExpressionContext, nothing to do
11581150
}
11591151
finally {
11601152
processingStateStack.pop();

0 commit comments

Comments
 (0)