2222import org .hibernate .query .sqm .SqmPathSource ;
2323import org .hibernate .query .sqm .tree .domain .SqmDomainType ;
2424import org .hibernate .query .sqm .tree .domain .SqmPluralPersistentAttribute ;
25+ import org .hibernate .query .sqm .tree .select .SqmSelectQuery ;
26+ import org .hibernate .query .sqm .tree .select .SqmSelection ;
2527import org .hibernate .query .sqm .tuple .TupleType ;
2628import org .hibernate .query .SemanticException ;
2729import org .hibernate .query .sqm .SqmExpressible ;
2830import org .hibernate .query .sqm .tree .domain .SqmPath ;
2931import org .hibernate .query .sqm .tree .select .SqmSelectClause ;
3032import org .hibernate .query .sqm .tree .select .SqmSelectableNode ;
31- import org .hibernate .query .sqm .tree .select .SqmSubQuery ;
3233import org .hibernate .spi .NavigablePath ;
3334import org .hibernate .sql .ast .spi .FromClauseAccess ;
3435import org .hibernate .sql .ast .spi .SqlSelection ;
@@ -55,26 +56,47 @@ public class AnonymousTupleType<T>
5556 private final String [] componentNames ;
5657 private final Map <String , Integer > componentIndexMap ;
5758
58- public AnonymousTupleType (SqmSubQuery <T > subQuery ) {
59- this ( extractSqmExpressibles ( subQuery ) );
60- }
59+ public AnonymousTupleType (SqmSelectQuery <T > selectQuery ) {
60+ final SqmSelectClause selectClause = selectQuery .getQueryPart ()
61+ .getFirstQuerySpec ()
62+ .getSelectClause ();
6163
62- public AnonymousTupleType (SqmSelectableNode <?>[] components ) {
63- expressibles = new SqmBindableType <?>[components .length ];
64- componentSourcePaths = new NavigablePath [components .length ];
65- for ( int i = 0 ; i < components .length ; i ++ ) {
66- expressibles [i ] = components [i ].getNodeType ();
67- if ( components [i ] instanceof SqmPath <?> path ) {
68- componentSourcePaths [i ] = path .getNavigablePath ();
64+ if ( selectClause == null || selectClause .getSelections ().isEmpty () ) {
65+ throw new IllegalArgumentException ( "selectQuery has no selection items" );
66+ }
67+ // todo: right now, we "snapshot" the state of the selectQuery when creating this type, but maybe we shouldn't?
68+ // i.e. what if the selectQuery changes later on? Or should we somehow mark the selectQuery to signal,
69+ // that changes to the select clause are invalid after a certain point?
70+
71+ final List <SqmSelection <?>> selections = selectClause .getSelections ();
72+ final List <SqmSelectableNode <?>> selectableNodes = new ArrayList <>();
73+ final List <String > aliases = new ArrayList <>();
74+ for ( SqmSelection <?> selection : selections ) {
75+ final boolean compound = selection .getSelectableNode ().isCompoundSelection ();
76+ selection .getSelectableNode ().visitSubSelectableNodes ( node -> {
77+ selectableNodes .add ( node );
78+ if ( compound ) {
79+ aliases .add ( node .getAlias () );
80+ }
81+ } );
82+ if ( !compound ) {
83+ // for compound selections we use the sub-selectable nodes aliases
84+ aliases .add ( selection .getAlias () );
6985 }
7086 }
71- componentNames = new String [components .length ];
87+
88+ expressibles = new SqmBindableType <?>[selectableNodes .size ()];
89+ componentSourcePaths = new NavigablePath [selectableNodes .size ()];
90+ componentNames = new String [selectableNodes .size ()];
7291 //noinspection unchecked
73- javaTypeDescriptor = (JavaType <T >) new ObjectArrayJavaType ( getTypeDescriptors ( components ) );
74- componentIndexMap = linkedMapOfSize ( components .length );
75- for ( int i = 0 ; i < components .length ; i ++ ) {
76- final SqmSelectableNode <?> component = components [i ];
77- final String alias = component .getAlias ();
92+ javaTypeDescriptor = (JavaType <T >) new ObjectArrayJavaType ( getTypeDescriptors ( selectableNodes ) );
93+ componentIndexMap = linkedMapOfSize ( selectableNodes .size () );
94+ for ( int i = 0 ; i < selectableNodes .size (); i ++ ) {
95+ expressibles [i ] = selectableNodes .get ( i ).getNodeType ();
96+ if ( selectableNodes .get ( i ) instanceof SqmPath <?> path ) {
97+ componentSourcePaths [i ] = path .getNavigablePath ();
98+ }
99+ String alias = aliases .get ( i );
78100 if ( alias == null ) {
79101 throw new SemanticException ( "Select item at position " + (i +1 ) + " in select list has no alias"
80102 + " (aliases are required in CTEs and in subqueries occurring in from clause)" );
@@ -110,21 +132,10 @@ public String getTypeName() {
110132 return SqmDomainType .super .getTypeName ();
111133 }
112134
113- private static SqmSelectableNode <?>[] extractSqmExpressibles (SqmSubQuery <?> subQuery ) {
114- final SqmSelectClause selectClause = subQuery .getQuerySpec ().getSelectClause ();
115- if ( selectClause == null || selectClause .getSelectionItems ().isEmpty () ) {
116- throw new IllegalArgumentException ( "subquery has no selection items" );
117- }
118- // todo: right now, we "snapshot" the state of the subquery when creating this type, but maybe we shouldn't?
119- // i.e. what if the subquery changes later on? Or should we somehow mark the subquery to signal,
120- // that changes to the select clause are invalid after a certain point?
121- return selectClause .getSelectionItems ().toArray ( SqmSelectableNode []::new );
122- }
123-
124- private static JavaType <?>[] getTypeDescriptors (SqmSelectableNode <?>[] components ) {
125- final JavaType <?>[] typeDescriptors = new JavaType <?>[components .length ];
126- for ( int i = 0 ; i < components .length ; i ++ ) {
127- typeDescriptors [i ] = components [i ].getExpressible ().getExpressibleJavaType ();
135+ private static JavaType <?>[] getTypeDescriptors (List <SqmSelectableNode <?>> components ) {
136+ final JavaType <?>[] typeDescriptors = new JavaType <?>[components .size ()];
137+ for ( int i = 0 ; i < components .size (); i ++ ) {
138+ typeDescriptors [i ] = components .get ( i ).getExpressible ().getExpressibleJavaType ();
128139 }
129140 return typeDescriptors ;
130141 }
0 commit comments