Skip to content

Commit 4870f9c

Browse files
committed
HHH-18816 FK rendering: check parent query specs for group by / order by
1 parent b9e47aa commit 4870f9c

File tree

4 files changed

+81
-43
lines changed

4 files changed

+81
-43
lines changed

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.hibernate.engine.spi.SessionFactoryImplementor;
2525
import org.hibernate.engine.spi.SharedSessionContractImplementor;
2626
import org.hibernate.internal.util.collections.CollectionHelper;
27+
import org.hibernate.internal.util.collections.Stack;
2728
import org.hibernate.metamodel.MappingMetamodel;
2829
import org.hibernate.metamodel.mapping.BasicValuedMapping;
2930
import org.hibernate.metamodel.mapping.Bindable;
@@ -174,38 +175,66 @@ public static ModelPartContainer getTargetMappingIfNeeded(
174175
SqmPath<?> sqmPath,
175176
ModelPartContainer modelPartContainer,
176177
SqmToSqlAstConverter sqlAstCreationState) {
177-
final SqmQueryPart<?> queryPart = sqlAstCreationState.getCurrentSqmQueryPart();
178-
if ( queryPart != null ) {
179-
// We only need to do this for queries
180-
final Clause clause = sqlAstCreationState.getCurrentClauseStack().getCurrent();
181-
if ( clause != Clause.FROM && modelPartContainer.getPartMappingType() != modelPartContainer && sqmPath.getLhs() instanceof SqmFrom<?, ?> ) {
182-
final ModelPart modelPart;
183-
if ( modelPartContainer instanceof PluralAttributeMapping ) {
184-
modelPart = getCollectionPart(
185-
(PluralAttributeMapping) modelPartContainer,
186-
castNonNull( sqmPath.getNavigablePath().getParent() )
187-
);
188-
}
189-
else {
190-
modelPart = modelPartContainer;
191-
}
192-
if ( modelPart instanceof EntityAssociationMapping ) {
193-
final EntityAssociationMapping association = (EntityAssociationMapping) modelPart;
194-
// If the path is one of the association's target key properties,
195-
// we need to render the target side if in group/order by
196-
if ( association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() )
197-
&& ( clause == Clause.GROUP || clause == Clause.ORDER
198-
|| !isFkOptimizationAllowed( sqmPath.getLhs(), association )
199-
|| queryPart.getFirstQuerySpec().groupByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState )
200-
|| queryPart.getFirstQuerySpec().orderByClauseContains( sqmPath.getNavigablePath(), sqlAstCreationState ) ) ) {
201-
return association.getAssociatedEntityMappingType();
202-
}
178+
// We only need to do this for queries
179+
if ( sqlAstCreationState.getCurrentClauseStack().getCurrent() != Clause.FROM
180+
&& modelPartContainer.getPartMappingType() != modelPartContainer && sqmPath.getLhs() instanceof SqmFrom<?, ?> ) {
181+
final ModelPart modelPart;
182+
if ( modelPartContainer instanceof PluralAttributeMapping ) {
183+
modelPart = getCollectionPart(
184+
(PluralAttributeMapping) modelPartContainer,
185+
castNonNull( sqmPath.getNavigablePath().getParent() )
186+
);
187+
}
188+
else {
189+
modelPart = modelPartContainer;
190+
}
191+
if ( modelPart instanceof EntityAssociationMapping ) {
192+
final EntityAssociationMapping association = (EntityAssociationMapping) modelPart;
193+
if ( shouldRenderTargetSide( sqmPath, association, sqlAstCreationState ) ) {
194+
return association.getAssociatedEntityMappingType();
203195
}
204196
}
205197
}
206198
return modelPartContainer;
207199
}
208200

201+
private static boolean shouldRenderTargetSide(
202+
SqmPath<?> sqmPath,
203+
EntityAssociationMapping association,
204+
SqmToSqlAstConverter sqlAstCreationState) {
205+
if ( !association.getTargetKeyPropertyNames().contains( sqmPath.getReferencedPathSource().getPathName() ) ) {
206+
return false;
207+
}
208+
// If the path is one of the association's target key properties,
209+
// we need to render the target side if in group/order by
210+
final Clause clause = sqlAstCreationState.getCurrentClauseStack().getCurrent();
211+
return clause == Clause.GROUP || clause == Clause.ORDER
212+
|| !isFkOptimizationAllowed( sqmPath.getLhs(), association )
213+
|| clauseContainsPath( Clause.GROUP, sqmPath, sqlAstCreationState )
214+
|| clauseContainsPath( Clause.ORDER, sqmPath, sqlAstCreationState );
215+
}
216+
217+
private static boolean clauseContainsPath(
218+
Clause clauseToCheck,
219+
SqmPath<?> sqmPath,
220+
SqmToSqlAstConverter sqlAstCreationState) {
221+
final Stack<SqmQueryPart> queryPartStack = sqlAstCreationState.getSqmQueryPartStack();
222+
final NavigablePath navigablePath = sqmPath.getNavigablePath();
223+
final Boolean found = queryPartStack.findCurrentFirst( queryPart -> {
224+
final SqmQuerySpec<?> spec = queryPart.getFirstQuerySpec();
225+
if ( clauseToCheck == Clause.GROUP && spec.groupByClauseContains( navigablePath, sqlAstCreationState ) ) {
226+
return true;
227+
}
228+
else if ( clauseToCheck == Clause.ORDER && spec.orderByClauseContains( navigablePath, sqlAstCreationState ) ) {
229+
return true;
230+
}
231+
else {
232+
return null;
233+
}
234+
} );
235+
return Boolean.TRUE.equals( found );
236+
}
237+
209238
private static CollectionPart getCollectionPart(PluralAttributeMapping attribute, NavigablePath path) {
210239
final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( path.getLocalName() );
211240
if ( nature != null ) {
@@ -214,6 +243,8 @@ private static CollectionPart getCollectionPart(PluralAttributeMapping attribute
214243
return attribute.getElementDescriptor();
215244
case INDEX:
216245
return attribute.getIndexDescriptor();
246+
case ID:
247+
return attribute.getIdentifierDescriptor();
217248
}
218249
}
219250
return null;

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

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
478478
private boolean deduplicateSelectionItems;
479479
private ForeignKeyDescriptor.Nature currentlyResolvingForeignKeySide;
480480
private SqmStatement<?> currentSqmStatement;
481-
private SqmQueryPart<?> currentSqmQueryPart;
481+
private Stack<SqmQueryPart> sqmQueryPartStack = new StandardStack<>( SqmQueryPart.class );
482482
private CteContainer cteContainer;
483483
/**
484484
* A map from {@link SqmCteTable#getCteName()} to the final SQL name.
@@ -784,9 +784,10 @@ public Stack<Clause> getCurrentClauseStack() {
784784
return currentClauseStack;
785785
}
786786

787+
@SuppressWarnings("rawtypes")
787788
@Override
788-
public SqmQueryPart<?> getCurrentSqmQueryPart() {
789-
return currentSqmQueryPart;
789+
public Stack<SqmQueryPart> getSqmQueryPartStack() {
790+
return sqmQueryPartStack;
790791
}
791792

792793
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1735,8 +1736,7 @@ public CteStatement visitCteStatement(SqmCteStatement<?> sqmCteStatement) {
17351736
);
17361737
final DelegatingSqmAliasedNodeCollector collector = (DelegatingSqmAliasedNodeCollector) processingState
17371738
.getSqlExpressionResolver();
1738-
final SqmQueryPart<?> oldSqmQueryPart = currentSqmQueryPart;
1739-
currentSqmQueryPart = queryGroup;
1739+
sqmQueryPartStack.push( queryGroup );
17401740
pushProcessingState( processingState );
17411741

17421742
try {
@@ -1780,7 +1780,7 @@ public CteStatement visitCteStatement(SqmCteStatement<?> sqmCteStatement) {
17801780
}
17811781
finally {
17821782
popProcessingStateStack();
1783-
currentSqmQueryPart = oldSqmQueryPart;
1783+
sqmQueryPartStack.pop();
17841784
}
17851785
}
17861786
finally {
@@ -1993,8 +1993,7 @@ public QueryGroup visitQueryGroup(SqmQueryGroup<?> queryGroup) {
19931993
);
19941994
final DelegatingSqmAliasedNodeCollector collector = (DelegatingSqmAliasedNodeCollector) processingState
19951995
.getSqlExpressionResolver();
1996-
final SqmQueryPart<?> sqmQueryPart = currentSqmQueryPart;
1997-
currentSqmQueryPart = queryGroup;
1996+
sqmQueryPartStack.push( queryGroup );
19981997
pushProcessingState( processingState );
19991998

20001999
try {
@@ -2016,7 +2015,7 @@ public QueryGroup visitQueryGroup(SqmQueryGroup<?> queryGroup) {
20162015
}
20172016
finally {
20182017
popProcessingStateStack();
2019-
currentSqmQueryPart = sqmQueryPart;
2018+
sqmQueryPartStack.pop();
20202019
}
20212020
}
20222021

@@ -2076,9 +2075,8 @@ else if ( sqmQuerySpec.hasPositionalGroupItem() ) {
20762075
);
20772076
}
20782077

2079-
final SqmQueryPart<?> sqmQueryPart = currentSqmQueryPart;
20802078
final boolean originalDeduplicateSelectionItems = deduplicateSelectionItems;
2081-
currentSqmQueryPart = sqmQuerySpec;
2079+
sqmQueryPartStack.push( sqmQuerySpec );
20822080
// In sub-queries, we can never deduplicate the selection items as that might change semantics
20832081
deduplicateSelectionItems = false;
20842082
pushProcessingState( processingState );
@@ -2145,7 +2143,7 @@ else if ( sqmQuerySpec.hasPositionalGroupItem() ) {
21452143
inNestedContext = oldInNestedContext;
21462144
popProcessingStateStack();
21472145
queryTransformers.pop();
2148-
currentSqmQueryPart = sqmQueryPart;
2146+
sqmQueryPartStack.pop();
21492147
deduplicateSelectionItems = originalDeduplicateSelectionItems;
21502148
}
21512149
}
@@ -2211,7 +2209,7 @@ public SelectClause visitSelectClause(SqmSelectClause selectClause) {
22112209
try {
22122210
final SelectClause sqlSelectClause = currentQuerySpec().getSelectClause();
22132211
if ( selectClause == null ) {
2214-
final SqmFrom<?, ?> implicitSelection = determineImplicitSelection( (SqmQuerySpec<?>) currentSqmQueryPart );
2212+
final SqmFrom<?, ?> implicitSelection = determineImplicitSelection( (SqmQuerySpec<?>) getCurrentSqmQueryPart() );
22152213
visitSelection( 0, new SqmSelection<>( implicitSelection, implicitSelection.nodeBuilder() ) );
22162214
}
22172215
else {
@@ -2236,7 +2234,7 @@ public SelectClause visitSelectClause(SqmSelectClause selectClause) {
22362234
@Override
22372235
public Void visitSelection(SqmSelection<?> sqmSelection) {
22382236
return visitSelection(
2239-
currentSqmQueryPart.getFirstQuerySpec().getSelectClause().getSelections().indexOf( sqmSelection ),
2237+
getCurrentSqmQueryPart().getFirstQuerySpec().getSelectClause().getSelections().indexOf( sqmSelection ),
22402238
sqmSelection
22412239
);
22422240
}
@@ -2355,7 +2353,7 @@ else if ( statement.getQuerySource() == SqmQuerySource.CRITERIA && currentClause
23552353
// To avoid this issue, we determine the position and let the SqlAstTranslator handle the rest.
23562354
// Usually it will render `select ?, count(*) from dual group by 1` if supported
23572355
// or force rendering the parameter as literal instead so that the database can see the grouping is fine
2358-
final SqmQuerySpec<?> querySpec = currentSqmQueryPart.getFirstQuerySpec();
2356+
final SqmQuerySpec<?> querySpec = getCurrentSqmQueryPart().getFirstQuerySpec();
23592357
sqmPosition = indexOfExpression( querySpec.getSelectClause().getSelections(), groupByClauseExpression );
23602358
path = null;
23612359
}
@@ -2383,7 +2381,7 @@ else if ( statement.getQuerySource() == SqmQuerySource.CRITERIA && currentClause
23832381
continue OUTER;
23842382
}
23852383
}
2386-
if ( currentSqmQueryPart instanceof SqmQueryGroup<?> ) {
2384+
if ( getCurrentSqmQueryPart() instanceof SqmQueryGroup<?> ) {
23872385
// Reusing the SqlSelection for query groups would be wrong because the aliases do no exist
23882386
// So we have to use a literal expression in a new SqlSelection instance to refer to the position
23892387
expressions.add(

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ public Stack<Clause> getCurrentClauseStack() {
9595
return null;
9696
}
9797

98+
@Override
99+
public Stack<SqmQueryPart> getSqmQueryPartStack() {
100+
return null;
101+
}
102+
98103
@Override
99104
public SqmQueryPart<?> getCurrentSqmQueryPart() {
100105
return null;

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@
3535
public interface SqmToSqlAstConverter extends SemanticQueryWalker<Object>, SqlAstCreationState {
3636
Stack<Clause> getCurrentClauseStack();
3737

38-
SqmQueryPart<?> getCurrentSqmQueryPart();
38+
Stack<SqmQueryPart> getSqmQueryPartStack();
39+
40+
default SqmQueryPart<?> getCurrentSqmQueryPart() {
41+
return getSqmQueryPartStack().getCurrent();
42+
}
3943

4044
void registerQueryTransformer(QueryTransformer transformer);
4145

0 commit comments

Comments
 (0)