Skip to content

Commit 64fc3ec

Browse files
committed
HHH-17379 HHH-17397 Improve check for non-optimizable path expressions
1 parent ced1d5f commit 64fc3ec

File tree

4 files changed

+50
-21
lines changed

4 files changed

+50
-21
lines changed

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

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.hibernate.query.sqm.tree.expression.SqmParameter;
5656
import org.hibernate.query.sqm.tree.from.SqmFrom;
5757
import org.hibernate.query.sqm.tree.from.SqmJoin;
58+
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
5859
import org.hibernate.query.sqm.tree.from.SqmRoot;
5960
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
6061
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
@@ -127,27 +128,62 @@ public static IllegalQueryOperationException expectingNonSelect(SqmStatement<?>
127128
);
128129
}
129130

131+
/**
132+
* Utility that returns {@code true} if the specified {@link SqmPath sqmPath} has to be
133+
* dereferenced using the target table mapping, and does not support fk optimization.
134+
* <p>
135+
* This is the case when the path is used in both the {@linkplain Clause#GROUP group by} clause
136+
* and a clause which {@linkplain #isClauseDependantOnGroupBy depends on it} or when the left
137+
* hand side of the path is a {@linkplain #isNonOptimizableJoin non-optimizable join}.
138+
*/
130139
public static boolean needsTargetTableMapping(
131140
SqmPath<?> sqmPath,
132141
ModelPartContainer modelPartContainer,
133142
SqmToSqlAstConverter sqlAstCreationState) {
134-
final Clause currentClause = sqlAstCreationState.getCurrentClauseStack().getCurrent();
135-
return ( currentClause == Clause.GROUP || currentClause == Clause.SELECT || currentClause == Clause.ORDER || currentClause == Clause.HAVING )
136-
&& modelPartContainer.getPartMappingType() != modelPartContainer
143+
return modelPartContainer.getPartMappingType() != modelPartContainer
137144
&& sqmPath.getLhs() instanceof SqmFrom<?, ?>
138145
&& modelPartContainer.getPartMappingType() instanceof ManagedMappingType
139-
&& ( groupByClauseContains( sqlAstCreationState.getCurrentSqmQueryPart(), sqmPath.getNavigablePath() )
146+
&& ( groupByClauseContains( sqlAstCreationState, sqmPath.getNavigablePath() )
140147
|| isNonOptimizableJoin( sqmPath.getLhs() ) );
141148
}
142149

143-
private static boolean groupByClauseContains(SqmQueryPart<?> sqmQueryPart, NavigablePath path) {
144-
return sqmQueryPart.isSimpleQueryPart() && sqmQueryPart.getFirstQuerySpec().groupByClauseContains( path );
150+
private static boolean groupByClauseContains(SqmToSqlAstConverter sqlAstCreationState, NavigablePath path) {
151+
if ( isClauseDependantOnGroupBy( sqlAstCreationState.getCurrentClauseStack().getCurrent() ) ) {
152+
final SqmQueryPart<?> sqmQueryPart = sqlAstCreationState.getCurrentSqmQueryPart();
153+
return sqmQueryPart.isSimpleQueryPart() && sqmQueryPart.getFirstQuerySpec().groupByClauseContains( path );
154+
}
155+
return false;
156+
}
157+
158+
private static boolean isClauseDependantOnGroupBy(Clause clause) {
159+
switch ( clause ) {
160+
case SELECT:
161+
case GROUP:
162+
case ORDER:
163+
case HAVING:
164+
return true;
165+
default:
166+
return false;
167+
}
145168
}
146169

147-
private static boolean isNonOptimizableJoin(SqmPath<?> sqmPath) {
170+
/**
171+
* Utility that returns {@code true} when the provided {@link SqmPath sqmPath} is
172+
* a join that cannot be dereferenced through the foreign key on the associated table,
173+
* i.e. a join that's neither {@linkplain SqmJoinType#INNER} nor {@linkplain SqmJoinType#LEFT}
174+
* or one that has an explicit on clause predicate.
175+
*/
176+
public static boolean isNonOptimizableJoin(SqmPath<?> sqmPath) {
148177
if ( sqmPath instanceof SqmJoin<?, ?> ) {
149-
final SqmJoinType sqmJoinType = ( (SqmJoin<?, ?>) sqmPath ).getSqmJoinType();
150-
return sqmJoinType != SqmJoinType.INNER && sqmJoinType != SqmJoinType.LEFT;
178+
final SqmJoin<?, ?> sqmJoin = (SqmJoin<?, ?>) sqmPath;
179+
switch ( sqmJoin.getSqmJoinType() ) {
180+
case INNER:
181+
case LEFT:
182+
return sqmJoin instanceof SqmQualifiedJoin<?, ?>
183+
&& ( (SqmQualifiedJoin<?, ?>) sqmJoin ).getJoinPredicate() != null;
184+
default:
185+
return true;
186+
}
151187
}
152188
return false;
153189
}

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@
431431
import static org.hibernate.query.sqm.TemporalUnit.NATIVE;
432432
import static org.hibernate.query.sqm.TemporalUnit.SECOND;
433433
import static org.hibernate.query.sqm.UnaryArithmeticOperator.UNARY_MINUS;
434+
import static org.hibernate.query.sqm.internal.SqmUtil.isNonOptimizableJoin;
434435
import static org.hibernate.sql.ast.spi.SqlAstTreeHelper.combinePredicates;
435436
import static org.hibernate.type.spi.TypeConfiguration.isDuration;
436437

@@ -3998,10 +3999,6 @@ public Expression visitQualifiedEntityJoin(SqmEntityJoin<?> sqmJoin) {
39983999
throw new InterpretationException( "SqmEntityJoin not yet resolved to TableGroup" );
39994000
}
40004001

4001-
private boolean isJoinWithPredicate(SqmFrom<?, ?> path) {
4002-
return path instanceof SqmQualifiedJoin && ( (SqmQualifiedJoin<?, ?>) path ).getJoinPredicate() != null;
4003-
}
4004-
40054002
private Expression visitTableGroup(TableGroup tableGroup, SqmFrom<?, ?> path) {
40064003
final ModelPartContainer tableGroupModelPart = tableGroup.getModelPart();
40074004

@@ -4028,9 +4025,9 @@ private Expression visitTableGroup(TableGroup tableGroup, SqmFrom<?, ?> path) {
40284025
// expansion to all target columns for select and group by clauses is handled in EntityValuedPathInterpretation
40294026
if ( entityValuedModelPart instanceof EntityAssociationMapping
40304027
&& ( (EntityAssociationMapping) entityValuedModelPart ).isFkOptimizationAllowed()
4031-
&& !isJoinWithPredicate( path ) ) {
4028+
&& !isNonOptimizableJoin( path ) ) {
40324029
// If the table group uses an association mapping that is not a one-to-many,
4033-
// we make use of the FK model part - unless the path is a join with an explicit predicate,
4030+
// we make use of the FK model part - unless the path is a non-optimizable join,
40344031
// for which we should always use the target's identifier to preserve semantics
40354032
final EntityAssociationMapping associationMapping = (EntityAssociationMapping) entityValuedModelPart;
40364033
final ModelPart targetPart = associationMapping.getForeignKeyDescriptor().getPart(

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,7 @@ public static <T> BasicValuedPathInterpretation<T> from(
8383

8484
final BasicValuedModelPart mapping;
8585
if ( needsTargetTableMapping( sqmPath, modelPartContainer, sqlAstCreationState ) ) {
86-
// In the select, group by, order by and having clause we have to make sure we render
87-
// the column of the target table, never the FK column, if the lhs is a join type that
88-
// requires it (right, full) or if this path is contained in group by clause
86+
// We have to make sure we render the column of the target table
8987
mapping = (BasicValuedModelPart) ( (ManagedMappingType) modelPartContainer.getPartMappingType() ).findSubPart(
9088
sqmPath.getReferencedPathSource().getPathName(),
9189
treatTarget

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ else if ( lhs.getNodeType() instanceof EntityDomainType ) {
6767
final ModelPartContainer modelPartContainer = tableGroup.getModelPart();
6868
final EmbeddableValuedModelPart mapping;
6969
if ( needsTargetTableMapping( sqmPath, modelPartContainer, sqlAstCreationState ) ) {
70-
// In the select, group by, order by and having clause we have to make sure we render
71-
// the column of the target table, never the FK column, if the lhs is a join type that
72-
// requires it (right, full) or if this path is contained in group by clause
70+
// We have to make sure we render the column of the target table
7371
mapping = (EmbeddableValuedModelPart) ( (ManagedMappingType) modelPartContainer.getPartMappingType() ).findSubPart(
7472
sqmPath.getReferencedPathSource().getPathName(),
7573
treatTarget

0 commit comments

Comments
 (0)