HHH-16409 Rework entity valued path expansion for group by and order by
This commit is contained in:
parent
3958a0d487
commit
4ad7662032
|
@ -751,6 +751,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return currentClauseStack;
|
return currentClauseStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmQueryPart<?> getCurrentSqmQueryPart() {
|
||||||
|
return currentSqmQueryPart;
|
||||||
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Statements
|
// Statements
|
||||||
|
@ -2395,46 +2399,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
return -( offset + selections.size() );
|
return -( offset + selections.size() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean selectClauseContains(SqmFrom<?, ?> from) {
|
|
||||||
final SqmQuerySpec<?> sqmQuerySpec = (SqmQuerySpec<?>) currentSqmQueryPart;
|
|
||||||
final List<SqmSelection<?>> selections = sqmQuerySpec.getSelectClause() == null
|
|
||||||
? Collections.emptyList()
|
|
||||||
: sqmQuerySpec.getSelectClause().getSelections();
|
|
||||||
if ( selections.isEmpty() && from instanceof SqmRoot<?> ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for ( SqmSelection<?> selection : selections ) {
|
|
||||||
if ( selectableNodeContains( selection.getSelectableNode(), from ) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean selectableNodeContains(SqmSelectableNode<?> selectableNode, SqmFrom<?, ?> from) {
|
|
||||||
if ( selectableNode == from ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if ( selectableNode instanceof SqmDynamicInstantiation ) {
|
|
||||||
for ( SqmDynamicInstantiationArgument<?> argument : ( (SqmDynamicInstantiation<?>) selectableNode ).getArguments() ) {
|
|
||||||
if ( selectableNodeContains( argument.getSelectableNode(), from ) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean groupByClauseContains(SqmFrom<?, ?> from) {
|
|
||||||
final SqmQuerySpec<?> sqmQuerySpec = (SqmQuerySpec<?>) currentSqmQueryPart;
|
|
||||||
for ( SqmExpression<?> expression : sqmQuerySpec.getGroupByClauseExpressions() ) {
|
|
||||||
if ( expression == from ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Expression> visitGroupByClause(List<SqmExpression<?>> groupByClauseExpressions) {
|
public List<Expression> visitGroupByClause(List<SqmExpression<?>> groupByClauseExpressions) {
|
||||||
if ( !groupByClauseExpressions.isEmpty() ) {
|
if ( !groupByClauseExpressions.isEmpty() ) {
|
||||||
|
@ -3755,8 +3719,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
final EntityValuedModelPart interpretationModelPart;
|
final EntityValuedModelPart interpretationModelPart;
|
||||||
final TableGroup tableGroupToUse;
|
final TableGroup tableGroupToUse;
|
||||||
if ( inferredEntityMapping == null ) {
|
if ( inferredEntityMapping == null ) {
|
||||||
// When the inferred mapping is null, we try to resolve to the FK by default,
|
// When the inferred mapping is null, we try to resolve to the FK by default, which is fine because
|
||||||
// which is fine because expansion to all target columns for select and group by clauses is handled below
|
// expansion to all target columns for select and group by clauses is handled in EntityValuedPathInterpretation
|
||||||
if ( entityValuedModelPart instanceof EntityAssociationMapping && ( (EntityAssociationMapping) entityValuedModelPart ).isFkOptimizationAllowed() ) {
|
if ( entityValuedModelPart instanceof EntityAssociationMapping && ( (EntityAssociationMapping) entityValuedModelPart ).isFkOptimizationAllowed() ) {
|
||||||
// If the table group uses an association mapping that is not a one-to-many,
|
// If the table group uses an association mapping that is not a one-to-many,
|
||||||
// we make use of the FK model part
|
// we make use of the FK model part
|
||||||
|
@ -3883,22 +3847,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
tableGroupToUse = null;
|
tableGroupToUse = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean expandToAllColumns;
|
|
||||||
if ( currentClauseStack.getCurrent() == Clause.GROUP ) {
|
|
||||||
// When the table group is known to be fetched i.e. a fetch join
|
|
||||||
// but also when the from clause is part of the select clause
|
|
||||||
// we need to expand to all columns, as we also expand this to all columns in the select clause
|
|
||||||
expandToAllColumns = tableGroup.isFetched() || selectClauseContains( path );
|
|
||||||
}
|
|
||||||
else if ( currentClauseStack.getCurrent() == Clause.ORDER ) {
|
|
||||||
// We must ensure that the order by expression be expanded if the group by
|
|
||||||
// contained the same expression, and that was expanded as well
|
|
||||||
expandToAllColumns = groupByClauseContains( path ) && ( tableGroup.isFetched() || selectClauseContains( path ) );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expandToAllColumns = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final EntityMappingType treatedMapping;
|
final EntityMappingType treatedMapping;
|
||||||
if ( path instanceof SqmTreatedPath ) {
|
if ( path instanceof SqmTreatedPath ) {
|
||||||
treatedMapping = creationContext.getSessionFactory()
|
treatedMapping = creationContext.getSessionFactory()
|
||||||
|
@ -3913,7 +3861,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
result = EntityValuedPathInterpretation.from(
|
result = EntityValuedPathInterpretation.from(
|
||||||
navigablePath,
|
navigablePath,
|
||||||
tableGroupToUse == null ? tableGroup : tableGroupToUse,
|
tableGroupToUse == null ? tableGroup : tableGroupToUse,
|
||||||
expandToAllColumns ? null : resultModelPart,
|
resultModelPart,
|
||||||
interpretationModelPart,
|
interpretationModelPart,
|
||||||
treatedMapping,
|
treatedMapping,
|
||||||
this
|
this
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.hibernate.query.sqm.tree.SqmVisitableNode;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||||
|
@ -86,6 +87,11 @@ public class FakeSqmToSqlAstConverter extends BaseSemanticQueryWalker implements
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmQueryPart<?> getCurrentSqmQueryPart() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerQueryTransformer(QueryTransformer transformer) {
|
public void registerQueryTransformer(QueryTransformer transformer) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.query.sqm.tree.SqmVisitableNode;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||||
import org.hibernate.sql.ast.Clause;
|
import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
@ -30,6 +31,8 @@ import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
public interface SqmToSqlAstConverter extends SemanticQueryWalker<Object>, SqlAstCreationState {
|
public interface SqmToSqlAstConverter extends SemanticQueryWalker<Object>, SqlAstCreationState {
|
||||||
Stack<Clause> getCurrentClauseStack();
|
Stack<Clause> getCurrentClauseStack();
|
||||||
|
|
||||||
|
SqmQueryPart<?> getCurrentSqmQueryPart();
|
||||||
|
|
||||||
void registerQueryTransformer(QueryTransformer transformer);
|
void registerQueryTransformer(QueryTransformer transformer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,7 +27,15 @@ import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
import org.hibernate.query.derived.AnonymousTupleEntityValuedModelPart;
|
import org.hibernate.query.derived.AnonymousTupleEntityValuedModelPart;
|
||||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||||
|
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationArgument;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
||||||
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.spi.FromClauseAccess;
|
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||||
|
@ -284,10 +292,28 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
EntityValuedModelPart mapping,
|
EntityValuedModelPart mapping,
|
||||||
EntityValuedModelPart treatedMapping,
|
EntityValuedModelPart treatedMapping,
|
||||||
SqmToSqlAstConverter sqlAstCreationState) {
|
SqmToSqlAstConverter sqlAstCreationState) {
|
||||||
|
final boolean expandToAllColumns;
|
||||||
|
final Clause currentClause = sqlAstCreationState.getCurrentClauseStack().getCurrent();
|
||||||
|
if ( currentClause == Clause.GROUP || currentClause == Clause.ORDER ) {
|
||||||
|
final SqmQuerySpec<?> querySpec = (SqmQuerySpec<?>) sqlAstCreationState.getCurrentSqmQueryPart();
|
||||||
|
if ( currentClause == Clause.ORDER && !groupByClauseContains( navigablePath, querySpec ) ) {
|
||||||
|
// We must ensure that the order by expression be expanded but only if the group by
|
||||||
|
// contained the same expression, and that was expanded as well
|
||||||
|
expandToAllColumns = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// When the table group is selected and the navigablePath is selected we need to expand
|
||||||
|
// to all columns, as we also expand this to all columns in the select clause
|
||||||
|
expandToAllColumns = isSelected( tableGroup, navigablePath, querySpec );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expandToAllColumns = false;
|
||||||
|
}
|
||||||
|
|
||||||
final SqlExpressionResolver sqlExprResolver = sqlAstCreationState.getSqlExpressionResolver();
|
final SqlExpressionResolver sqlExprResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||||
final Expression sqlExpression;
|
final Expression sqlExpression;
|
||||||
|
if ( expandToAllColumns ) {
|
||||||
if ( resultModelPart == null ) {
|
|
||||||
// Expand to all columns of the entity mapping type, as we already did for the selection
|
// Expand to all columns of the entity mapping type, as we already did for the selection
|
||||||
final EntityMappingType entityMappingType = mapping.getEntityMappingType();
|
final EntityMappingType entityMappingType = mapping.getEntityMappingType();
|
||||||
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
||||||
|
@ -352,6 +378,47 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isSelected(TableGroup tableGroup, NavigablePath path, SqmQuerySpec<?> sqmQuerySpec) {
|
||||||
|
// If the table group is selected (initialized), check if the entity valued
|
||||||
|
// navigable path or any child path appears in the select clause
|
||||||
|
return tableGroup.isInitialized() && selectClauseContains( path, sqmQuerySpec );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean selectClauseContains(NavigablePath path, SqmQuerySpec<?> sqmQuerySpec) {
|
||||||
|
final List<SqmSelection<?>> selections = sqmQuerySpec.getSelectClause() == null
|
||||||
|
? Collections.emptyList()
|
||||||
|
: sqmQuerySpec.getSelectClause().getSelections();
|
||||||
|
for ( SqmSelection<?> selection : selections ) {
|
||||||
|
if ( selectableNodeContains( selection.getSelectableNode(), path ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean selectableNodeContains(SqmSelectableNode<?> selectableNode, NavigablePath path) {
|
||||||
|
if ( selectableNode instanceof SqmPath && path.isParentOrEqual( ( (SqmPath<?>) selectableNode ).getNavigablePath() ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ( selectableNode instanceof SqmDynamicInstantiation ) {
|
||||||
|
for ( SqmDynamicInstantiationArgument<?> argument : ( (SqmDynamicInstantiation<?>) selectableNode ).getArguments() ) {
|
||||||
|
if ( selectableNodeContains( argument.getSelectableNode(), path ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean groupByClauseContains(NavigablePath path, SqmQuerySpec<?> sqmQuerySpec) {
|
||||||
|
for ( SqmExpression<?> expression : sqmQuerySpec.getGroupByClauseExpressions() ) {
|
||||||
|
if ( expression instanceof SqmPath && ( (SqmPath<?>) expression ).getNavigablePath() == path ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private final Expression sqlExpression;
|
private final Expression sqlExpression;
|
||||||
|
|
||||||
public EntityValuedPathInterpretation(
|
public EntityValuedPathInterpretation(
|
||||||
|
|
Loading…
Reference in New Issue