22
22
import org .hibernate .query .sqm .SqmPathSource ;
23
23
import org .hibernate .query .sqm .tree .domain .SqmDomainType ;
24
24
import 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 ;
25
27
import org .hibernate .query .sqm .tuple .TupleType ;
26
28
import org .hibernate .query .SemanticException ;
27
29
import org .hibernate .query .sqm .SqmExpressible ;
28
30
import org .hibernate .query .sqm .tree .domain .SqmPath ;
29
31
import org .hibernate .query .sqm .tree .select .SqmSelectClause ;
30
32
import org .hibernate .query .sqm .tree .select .SqmSelectableNode ;
31
- import org .hibernate .query .sqm .tree .select .SqmSubQuery ;
32
33
import org .hibernate .spi .NavigablePath ;
33
34
import org .hibernate .sql .ast .spi .FromClauseAccess ;
34
35
import org .hibernate .sql .ast .spi .SqlSelection ;
@@ -55,26 +56,47 @@ public class AnonymousTupleType<T>
55
56
private final String [] componentNames ;
56
57
private final Map <String , Integer > componentIndexMap ;
57
58
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 ();
61
63
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 () );
69
85
}
70
86
}
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 ()];
72
91
//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 );
78
100
if ( alias == null ) {
79
101
throw new SemanticException ( "Select item at position " + (i +1 ) + " in select list has no alias"
80
102
+ " (aliases are required in CTEs and in subqueries occurring in from clause)" );
@@ -110,21 +132,10 @@ public String getTypeName() {
110
132
return SqmDomainType .super .getTypeName ();
111
133
}
112
134
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 ();
128
139
}
129
140
return typeDescriptors ;
130
141
}
0 commit comments