HHH-17225 Always use target table column for right / full joins

This commit is contained in:
Marco Belladelli 2023-09-25 10:53:22 +02:00 committed by Christian Beikov
parent 33d4f3adf4
commit b115ab7f42
3 changed files with 48 additions and 30 deletions

View File

@ -29,7 +29,9 @@ import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.IllegalSelectQueryException; import org.hibernate.query.IllegalSelectQueryException;
@ -42,7 +44,9 @@ import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmQuerySource; import org.hibernate.query.sqm.SqmQuerySource;
import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess; import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.SqmDmlStatement; import org.hibernate.query.sqm.tree.SqmDmlStatement;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
@ -50,11 +54,14 @@ import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode; import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
import org.hibernate.query.sqm.tree.select.SqmSortSpecification; import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlTreeCreationException; import org.hibernate.sql.ast.SqlTreeCreationException;
import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
@ -120,6 +127,31 @@ public class SqmUtil {
); );
} }
public static boolean needsTargetTableMapping(
SqmPath<?> sqmPath,
ModelPartContainer modelPartContainer,
SqmToSqlAstConverter sqlAstCreationState) {
final Clause currentClause = sqlAstCreationState.getCurrentClauseStack().getCurrent();
return ( currentClause == Clause.GROUP || currentClause == Clause.SELECT || currentClause == Clause.ORDER || currentClause == Clause.HAVING )
&& modelPartContainer.getPartMappingType() != modelPartContainer
&& sqmPath.getLhs() instanceof SqmFrom<?, ?>
&& modelPartContainer.getPartMappingType() instanceof ManagedMappingType
&& ( groupByClauseContains( sqlAstCreationState.getCurrentSqmQueryPart(), sqmPath.getNavigablePath() )
|| isNonOptimizableJoin( sqmPath.getLhs() ) );
}
private static boolean groupByClauseContains(SqmQueryPart<?> sqmQueryPart, NavigablePath path) {
return sqmQueryPart.isSimpleQueryPart() && sqmQueryPart.getFirstQuerySpec().groupByClauseContains( path );
}
private static boolean isNonOptimizableJoin(SqmPath<?> sqmPath) {
if ( sqmPath instanceof SqmJoin<?, ?> ) {
final SqmJoinType sqmJoinType = ( (SqmJoin<?, ?>) sqmPath ).getSqmJoinType();
return sqmJoinType != SqmJoinType.INNER && sqmJoinType != SqmJoinType.LEFT;
}
return false;
}
public static Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> generateJdbcParamsXref( public static Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> generateJdbcParamsXref(
DomainParameterXref domainParameterXref, DomainParameterXref domainParameterXref,
JdbcParameterBySqmParameterAccess jdbcParameterBySqmParameterAccess) { JdbcParameterBySqmParameterAccess jdbcParameterBySqmParameterAccess) {

View File

@ -25,10 +25,7 @@ import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
@ -37,6 +34,8 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.update.Assignable; import org.hibernate.sql.ast.tree.update.Assignable;
import static org.hibernate.query.sqm.internal.SqmUtil.needsTargetTableMapping;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -83,16 +82,10 @@ public class BasicValuedPathInterpretation<T> extends AbstractSqmPathInterpretat
} }
final BasicValuedModelPart mapping; final BasicValuedModelPart mapping;
// In the select, group by, order by and having clause we have to make sure we render the column of the target table, if ( needsTargetTableMapping( sqmPath, modelPartContainer, sqlAstCreationState ) ) {
// never the FK column, if the lhs is a SqmFrom i.e. something explicitly queried/joined // In the select, group by, order by and having clause we have to make sure we render
// and if this basic path is part of the group by clause // the column of the target table, never the FK column, if the lhs is a join type that
final Clause currentClause = sqlAstCreationState.getCurrentClauseStack().getCurrent(); // requires it (right, full) or if this path is contained in group by clause
final SqmQueryPart<?> sqmQueryPart = sqlAstCreationState.getCurrentSqmQueryPart();
if ( ( currentClause == Clause.GROUP || currentClause == Clause.SELECT || currentClause == Clause.ORDER || currentClause == Clause.HAVING )
&& lhs instanceof SqmFrom<?, ?>
&& modelPartContainer.getPartMappingType() instanceof ManagedMappingType
&& sqmQueryPart.isSimpleQueryPart()
&& sqmQueryPart.getFirstQuerySpec().groupByClauseContains( sqmPath.getNavigablePath() ) ) {
mapping = (BasicValuedModelPart) ( (ManagedMappingType) modelPartContainer.getPartMappingType() ).findSubPart( mapping = (BasicValuedModelPart) ( (ManagedMappingType) modelPartContainer.getPartMappingType() ).findSubPart(
sqmPath.getReferencedPathSource().getPathName(), sqmPath.getReferencedPathSource().getPathName(),
treatTarget treatTarget

View File

@ -20,10 +20,7 @@ import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
@ -32,6 +29,8 @@ import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.update.Assignable; import org.hibernate.sql.ast.tree.update.Assignable;
import static org.hibernate.query.sqm.internal.SqmUtil.needsTargetTableMapping;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -65,25 +64,19 @@ public class EmbeddableValuedPathInterpretation<T> extends AbstractSqmPathInterp
} }
} }
final ModelPartContainer modelPart = tableGroup.getModelPart(); final ModelPartContainer modelPartContainer = tableGroup.getModelPart();
final EmbeddableValuedModelPart mapping; final EmbeddableValuedModelPart mapping;
// In the select, group by, order by and having clause we have to make sure we render the column of the target table, if ( needsTargetTableMapping( sqmPath, modelPartContainer, sqlAstCreationState ) ) {
// never the FK column, if the lhs is a SqmFrom i.e. something explicitly queried/joined // In the select, group by, order by and having clause we have to make sure we render
// and if this basic path is part of the group by clause // the column of the target table, never the FK column, if the lhs is a join type that
final Clause currentClause = sqlAstCreationState.getCurrentClauseStack().getCurrent(); // requires it (right, full) or if this path is contained in group by clause
final SqmQueryPart<?> sqmQueryPart = sqlAstCreationState.getCurrentSqmQueryPart(); mapping = (EmbeddableValuedModelPart) ( (ManagedMappingType) modelPartContainer.getPartMappingType() ).findSubPart(
if ( ( currentClause == Clause.GROUP || currentClause == Clause.SELECT || currentClause == Clause.ORDER || currentClause == Clause.HAVING )
&& lhs instanceof SqmFrom<?, ?>
&& modelPart.getPartMappingType() instanceof ManagedMappingType
&& sqmQueryPart.isSimpleQueryPart()
&& sqmQueryPart.getFirstQuerySpec().groupByClauseContains( sqmPath.getNavigablePath() ) ) {
mapping = (EmbeddableValuedModelPart) ( (ManagedMappingType) modelPart.getPartMappingType() ).findSubPart(
sqmPath.getReferencedPathSource().getPathName(), sqmPath.getReferencedPathSource().getPathName(),
treatTarget treatTarget
); );
} }
else { else {
mapping = (EmbeddableValuedModelPart) modelPart.findSubPart( mapping = (EmbeddableValuedModelPart) modelPartContainer.findSubPart(
sqmPath.getReferencedPathSource().getPathName(), sqmPath.getReferencedPathSource().getPathName(),
treatTarget treatTarget
); );
@ -92,7 +85,7 @@ public class EmbeddableValuedPathInterpretation<T> extends AbstractSqmPathInterp
return new EmbeddableValuedPathInterpretation<>( return new EmbeddableValuedPathInterpretation<>(
mapping.toSqlExpression( mapping.toSqlExpression(
tableGroup, tableGroup,
currentClause, sqlAstCreationState.getCurrentClauseStack().getCurrent(),
sqlAstCreationState, sqlAstCreationState,
sqlAstCreationState sqlAstCreationState
), ),