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