Skip to content

Commit bf3e262

Browse files
committed
HHH-19396 Parameter deduplicateSelectionItems must be set only for temporary tables and subqueries
Selection items representing same column in (sub)query will be represented with single instance, this will hide real alias
1 parent 9430e1f commit bf3e262

File tree

3 files changed

+53
-41
lines changed

3 files changed

+53
-41
lines changed

hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleType.java

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
import jakarta.persistence.metamodel.Bindable;
1515
import org.hibernate.Incubating;
16-
import org.hibernate.internal.util.collections.CollectionHelper;
1716
import org.hibernate.metamodel.UnsupportedMappingException;
1817
import org.hibernate.metamodel.model.domain.DomainType;
1918
import org.hibernate.metamodel.model.domain.EntityDomainType;
@@ -28,16 +27,18 @@
2827
import org.hibernate.query.sqm.SqmPathSource;
2928
import org.hibernate.query.sqm.tree.domain.SqmPath;
3029
import org.hibernate.query.sqm.tree.select.SqmSelectClause;
30+
import org.hibernate.query.sqm.tree.select.SqmSelectQuery;
3131
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
32-
import org.hibernate.query.sqm.tree.select.SqmSubQuery;
32+
import org.hibernate.query.sqm.tree.select.SqmSelection;
3333
import org.hibernate.sql.ast.spi.FromClauseAccess;
3434
import org.hibernate.sql.ast.spi.SqlSelection;
35-
import org.hibernate.type.BasicType;
3635
import org.hibernate.type.descriptor.java.JavaType;
3736
import org.hibernate.type.descriptor.java.ObjectArrayJavaType;
3837

3938
import jakarta.persistence.metamodel.Attribute;
4039

40+
import static org.hibernate.internal.util.collections.CollectionHelper.linkedMapOfSize;
41+
4142

4243
/**
4344
* @author Christian Beikov
@@ -47,43 +48,58 @@ public class AnonymousTupleType<T> implements TupleType<T>, DomainType<T>, Retur
4748

4849
private final ObjectArrayJavaType javaTypeDescriptor;
4950
private final SqmSelectableNode<?>[] components;
51+
private final String[] componentNames;
5052
private final Map<String, Integer> componentIndexMap;
5153

52-
public AnonymousTupleType(SqmSubQuery<T> subQuery) {
53-
this( extractSqmExpressibles( subQuery ) );
54-
}
54+
public AnonymousTupleType(SqmSelectQuery<T> selectQuery) {
55+
final SqmSelectClause selectClause = selectQuery.getQueryPart()
56+
.getFirstQuerySpec()
57+
.getSelectClause();
5558

56-
public AnonymousTupleType(SqmSelectableNode<?>[] components) {
57-
this.components = components;
58-
this.javaTypeDescriptor = new ObjectArrayJavaType( getTypeDescriptors( components ) );
59-
final Map<String, Integer> map = CollectionHelper.linkedMapOfSize( components.length );
60-
for ( int i = 0; i < components.length; i++ ) {
61-
final SqmSelectableNode<?> component = components[i];
62-
final String alias = component.getAlias();
59+
if ( selectClause == null || selectClause.getSelections().isEmpty() ) {
60+
throw new IllegalArgumentException( "selectQuery has no selection items" );
61+
}
62+
// todo: right now, we "snapshot" the state of the selectQuery when creating this type, but maybe we shouldn't?
63+
// i.e. what if the selectQuery changes later on? Or should we somehow mark the selectQuery to signal,
64+
// that changes to the select clause are invalid after a certain point?
65+
66+
final List<SqmSelection<?>> selections = selectClause.getSelections();
67+
final List<SqmSelectableNode<?>> selectableNodes = new ArrayList<>();
68+
final List<String> aliases = new ArrayList<>();
69+
for ( SqmSelection<?> selection : selections ) {
70+
final boolean compound = selection.getSelectableNode().isCompoundSelection();
71+
selection.getSelectableNode().visitSubSelectableNodes( node -> {
72+
selectableNodes.add( node );
73+
if ( compound ) {
74+
aliases.add( node.getAlias() );
75+
}
76+
} );
77+
if ( !compound ) {
78+
// for compound selections we use the sub-selectable nodes aliases
79+
aliases.add( selection.getAlias() );
80+
}
81+
}
82+
83+
components = new SqmSelectableNode<?>[selectableNodes.size()];
84+
componentNames = new String[selectableNodes.size()];
85+
javaTypeDescriptor = new ObjectArrayJavaType( getTypeDescriptors( selectableNodes ) );
86+
componentIndexMap = linkedMapOfSize( selectableNodes.size() );
87+
for ( int i = 0; i < selectableNodes.size(); i++ ) {
88+
components[i] = selectableNodes.get(i);
89+
String alias = aliases.get( i );
6390
if ( alias == null ) {
6491
throw new SemanticException( "Select item at position " + (i+1) + " in select list has no alias"
6592
+ " (aliases are required in CTEs and in subqueries occurring in from clause)" );
6693
}
67-
map.put( alias, i );
68-
}
69-
this.componentIndexMap = map;
70-
}
71-
72-
private static SqmSelectableNode<?>[] extractSqmExpressibles(SqmSubQuery<?> subQuery) {
73-
final SqmSelectClause selectClause = subQuery.getQuerySpec().getSelectClause();
74-
if ( selectClause == null || selectClause.getSelectionItems().isEmpty() ) {
75-
throw new IllegalArgumentException( "subquery has no selection items" );
94+
componentIndexMap.put( alias, i );
95+
componentNames[i] = alias;
7696
}
77-
// todo: right now, we "snapshot" the state of the subquery when creating this type, but maybe we shouldn't?
78-
// i.e. what if the subquery changes later on? Or should we somehow mark the subquery to signal,
79-
// that changes to the select clause are invalid after a certain point?
80-
return selectClause.getSelectionItems().toArray( SqmSelectableNode[]::new );
8197
}
8298

83-
private static JavaType<?>[] getTypeDescriptors(SqmSelectableNode<?>[] components) {
84-
final JavaType<?>[] typeDescriptors = new JavaType<?>[components.length];
85-
for ( int i = 0; i < components.length; i++ ) {
86-
typeDescriptors[i] = components[i].getExpressible().getExpressibleJavaType();
99+
private static JavaType<?>[] getTypeDescriptors(List<SqmSelectableNode<?>> components) {
100+
final JavaType<?>[] typeDescriptors = new JavaType<?>[components.size()];
101+
for ( int i = 0; i < components.size(); i++ ) {
102+
typeDescriptors[i] = components.get( i ).getExpressible().getExpressibleJavaType();
87103
}
88104
return typeDescriptors;
89105
}
@@ -143,7 +159,7 @@ public int componentCount() {
143159

144160
@Override
145161
public String getComponentName(int index) {
146-
return components[index].getAlias();
162+
return componentNames[index];
147163
}
148164

149165
@Override

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,11 +1926,14 @@ public CteContainer visitCteContainer(SqmCteContainer consumer) {
19261926
final Collection<SqmCteStatement<?>> sqmCteStatements = consumer.getCteStatements();
19271927
cteContainer = new CteContainerImpl( cteContainer );
19281928
if ( !sqmCteStatements.isEmpty() ) {
1929+
final boolean originalDeduplicateSelectionItems = deduplicateSelectionItems;
1930+
deduplicateSelectionItems = false;
19291931
currentClauseStack.push( Clause.WITH );
19301932
for ( SqmCteStatement<?> sqmCteStatement : sqmCteStatements ) {
19311933
visitCteStatement( sqmCteStatement );
19321934
}
19331935
currentClauseStack.pop();
1936+
deduplicateSelectionItems = originalDeduplicateSelectionItems;
19341937
// Avoid leaking the processing state from CTEs to upper levels
19351938
lastPoppedFromClauseIndex = null;
19361939
lastPoppedProcessingState = null;

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteTable.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import org.hibernate.query.sqm.SqmExpressible;
1919
import org.hibernate.query.sqm.SqmPathSource;
2020
import org.hibernate.query.sqm.tree.select.SqmSelectQuery;
21-
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
22-
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
2321
import org.hibernate.sql.ast.spi.FromClauseAccess;
2422
import org.hibernate.sql.ast.spi.SqlSelection;
2523
import org.hibernate.type.BasicType;
@@ -36,8 +34,8 @@ public class SqmCteTable<T> extends AnonymousTupleType<T> implements JpaCteCrite
3634
private SqmCteTable(
3735
String name,
3836
SqmCteStatement<T> cteStatement,
39-
SqmSelectableNode<?>[] sqmSelectableNodes) {
40-
super( sqmSelectableNodes );
37+
SqmSelectQuery<T> selectStatement) {
38+
super(selectStatement);
4139
this.name = name;
4240
this.cteStatement = cteStatement;
4341
final List<SqmCteTableColumn> columns = new ArrayList<>( componentCount() );
@@ -57,12 +55,7 @@ public static <X> SqmCteTable<X> createStatementTable(
5755
String name,
5856
SqmCteStatement<X> cteStatement,
5957
SqmSelectQuery<X> selectStatement) {
60-
final SqmSelectableNode<?>[] sqmSelectableNodes = selectStatement.getQueryPart()
61-
.getFirstQuerySpec()
62-
.getSelectClause()
63-
.getSelectionItems()
64-
.toArray( SqmSelectableNode[]::new );
65-
return new SqmCteTable<>( name, cteStatement, sqmSelectableNodes );
58+
return new SqmCteTable<>( name, cteStatement, selectStatement );
6659
}
6760

6861
@Override

0 commit comments

Comments
 (0)