HHH-17001 Visit nested joins and predicates only once through special consume methods in BaseSemanticQueryWalker

This commit is contained in:
Christian Beikov 2023-08-07 11:08:36 +02:00
parent 861774fd8e
commit b3ec2cd943
2 changed files with 157 additions and 80 deletions

View File

@ -10,6 +10,7 @@ import java.util.List;
import org.hibernate.metamodel.model.domain.internal.AnyDiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.EntityDiscriminatorSqmPath;
import org.hibernate.query.sqm.InterpretationException;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.tree.SqmVisitableNode;
import org.hibernate.query.sqm.tree.cte.SqmCteContainer;
@ -74,7 +75,9 @@ import org.hibernate.query.sqm.tree.from.SqmCrossJoin;
import org.hibernate.query.sqm.tree.from.SqmCteJoin;
import org.hibernate.query.sqm.tree.from.SqmDerivedJoin;
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
@ -110,6 +113,8 @@ import org.hibernate.query.sqm.tree.select.SqmSubQuery;
import org.hibernate.query.sqm.tree.update.SqmAssignment;
import org.hibernate.query.sqm.tree.update.SqmSetClause;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.hibernate.sql.ast.spi.SqlAstQueryPartProcessingState;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.service.ServiceRegistry;
/**
@ -247,84 +252,166 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
@Override
public Object visitFromClause(SqmFromClause fromClause) {
fromClause.visitRoots( root -> root.accept( this ) );
fromClause.visitRoots( this::consumeFromClauseRoot );
return fromClause;
}
protected void consumeFromClauseRoot(SqmRoot<?> sqmRoot) {
if ( sqmRoot instanceof SqmDerivedRoot<?> ) {
( (SqmDerivedRoot<?>) sqmRoot ).getQueryPart().accept( this );
}
consumeJoins( sqmRoot );
}
private void consumeJoins(SqmRoot<?> sqmRoot) {
if ( sqmRoot.getOrderedJoins() == null ) {
consumeExplicitJoins( sqmRoot );
}
else {
for ( SqmJoin<?, ?> join : sqmRoot.getOrderedJoins() ) {
consumeExplicitJoin( join, false );
}
}
}
protected void consumeExplicitJoins(SqmFrom<?, ?> sqmFrom) {
sqmFrom.visitSqmJoins(
sqmJoin -> {
consumeExplicitJoin( sqmJoin, true );
}
);
final List<SqmFrom<?, ?>> sqmTreats = sqmFrom.getSqmTreats();
if ( !sqmTreats.isEmpty() ) {
for ( SqmFrom<?, ?> sqmTreat : sqmTreats ) {
consumeTreat( sqmTreat );
}
}
}
protected void consumeTreat(SqmFrom<?, ?> sqmTreat) {
consumeExplicitJoins( sqmTreat );
}
protected void consumeExplicitJoin(
SqmJoin<?, ?> sqmJoin,
boolean transitive) {
if ( sqmJoin instanceof SqmAttributeJoin<?, ?> ) {
consumeAttributeJoin( ( (SqmAttributeJoin<?, ?>) sqmJoin ), transitive );
}
else if ( sqmJoin instanceof SqmCrossJoin<?> ) {
consumeCrossJoin( ( (SqmCrossJoin<?>) sqmJoin ), transitive );
}
else if ( sqmJoin instanceof SqmEntityJoin<?> ) {
consumeEntityJoin( ( (SqmEntityJoin<?>) sqmJoin ), transitive );
}
else if ( sqmJoin instanceof SqmDerivedJoin<?> ) {
consumeDerivedJoin( ( (SqmDerivedJoin<?>) sqmJoin ), transitive );
}
else if ( sqmJoin instanceof SqmCteJoin<?> ) {
consumeCteJoin( ( (SqmCteJoin<?>) sqmJoin ), transitive );
}
else if ( sqmJoin instanceof SqmPluralPartJoin<?, ?> ) {
consumePluralPartJoin( ( (SqmPluralPartJoin<?, ?>) sqmJoin ), transitive );
}
else {
throw new InterpretationException( "Could not visit SqmJoin [" + sqmJoin.getNavigablePath() + "] of type [" + sqmJoin.getClass().getName() + "]" );
}
}
protected void consumeAttributeJoin(SqmAttributeJoin<?, ?> sqmJoin, boolean transitive) {
if ( sqmJoin.getJoinPredicate() != null ) {
sqmJoin.getJoinPredicate().accept( this );
}
if ( transitive ) {
consumeExplicitJoins( sqmJoin );
}
}
protected void consumeCrossJoin(SqmCrossJoin<?> sqmJoin, boolean transitive) {
if ( transitive ) {
consumeExplicitJoins( sqmJoin );
}
}
protected void consumeEntityJoin(SqmEntityJoin<?> sqmJoin, boolean transitive) {
if ( sqmJoin.getJoinPredicate() != null ) {
sqmJoin.getJoinPredicate().accept( this );
}
if ( transitive ) {
consumeExplicitJoins( sqmJoin );
}
}
protected void consumeDerivedJoin(SqmDerivedJoin<?> sqmJoin, boolean transitive) {
sqmJoin.getQueryPart().accept( this );
if ( sqmJoin.getJoinPredicate() != null ) {
sqmJoin.getJoinPredicate().accept( this );
}
if ( transitive ) {
consumeExplicitJoins( sqmJoin );
}
}
protected void consumeCteJoin(SqmCteJoin<?> sqmJoin, boolean transitive) {
if ( sqmJoin.getJoinPredicate() != null ) {
sqmJoin.getJoinPredicate().accept( this );
}
if ( transitive ) {
consumeExplicitJoins( sqmJoin );
}
}
protected void consumePluralPartJoin(SqmPluralPartJoin<?, ?> sqmJoin, boolean transitive) {
if ( sqmJoin.getJoinPredicate() != null ) {
sqmJoin.getJoinPredicate().accept( this );
}
if ( transitive ) {
consumeExplicitJoins( sqmJoin );
}
}
@Override
public Object visitRootPath(SqmRoot<?> sqmRoot) {
sqmRoot.visitReusablePaths( path -> path.accept( this ) );
sqmRoot.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return sqmRoot;
}
@Override
public Object visitRootDerived(SqmDerivedRoot<?> sqmRoot) {
sqmRoot.getQueryPart().accept( this );
sqmRoot.visitReusablePaths( path -> path.accept( this ) );
sqmRoot.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return sqmRoot;
}
@Override
public Object visitRootCte(SqmCteRoot<?> sqmRoot) {
sqmRoot.visitReusablePaths( path -> path.accept( this ) );
sqmRoot.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return sqmRoot;
}
@Override
public Object visitCrossJoin(SqmCrossJoin<?> joinedFromElement) {
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );
joinedFromElement.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return joinedFromElement;
}
@Override
public Object visitPluralPartJoin(SqmPluralPartJoin<?, ?> joinedFromElement) {
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );
joinedFromElement.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return joinedFromElement;
}
@Override
public Object visitQualifiedEntityJoin(SqmEntityJoin<?> joinedFromElement) {
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );
joinedFromElement.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
if ( joinedFromElement.getJoinPredicate() != null ) {
joinedFromElement.getJoinPredicate().accept( this );
}
return joinedFromElement;
}
@Override
public Object visitQualifiedAttributeJoin(SqmAttributeJoin<?,?> joinedFromElement) {
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );
joinedFromElement.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
if ( joinedFromElement.getJoinPredicate() != null ) {
joinedFromElement.getJoinPredicate().accept( this );
}
return joinedFromElement;
}
@Override
public Object visitQualifiedDerivedJoin(SqmDerivedJoin<?> joinedFromElement) {
joinedFromElement.getQueryPart().accept( this );
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );
joinedFromElement.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
if ( joinedFromElement.getJoinPredicate() != null ) {
joinedFromElement.getJoinPredicate().accept( this );
}
return joinedFromElement;
}
@Override
public Object visitQualifiedCteJoin(SqmCteJoin<?> joinedFromElement) {
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );
joinedFromElement.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
if ( joinedFromElement.getJoinPredicate() != null ) {
joinedFromElement.getJoinPredicate().accept( this );
}
return joinedFromElement;
}
@ -385,8 +472,6 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
@Override
public Object visitCorrelation(SqmCorrelation<?, ?> correlation) {
correlation.visitReusablePaths( path -> path.accept( this ) );
correlation.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return correlation;
}

View File

@ -16,6 +16,7 @@ import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.spi.BaseSemanticQueryWalker;
import org.hibernate.query.sqm.tree.SqmExpressibleAccessor;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.SqmVisitableNode;
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
@ -98,7 +99,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
@Override
public Object visitFunction(SqmFunction<?> sqmFunction) {
SqmExpressibleAccessor<?> current = inferenceBasis;
final SqmExpressibleAccessor<?> current = inferenceBasis;
inferenceBasis = null;
super.visitFunction( sqmFunction );
inferenceBasis = current;
@ -140,11 +141,11 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
private SqmExpressibleAccessor<?> inferenceBasis;
private void withTypeInference(SqmExpressibleAccessor<?> inferenceBasis, Runnable action) {
private void withTypeInference(SqmExpressibleAccessor<?> inferenceBasis, SqmVisitableNode sqmVisitableNode) {
SqmExpressibleAccessor<?> original = this.inferenceBasis;
this.inferenceBasis = inferenceBasis;
try {
action.run();
sqmVisitableNode.accept( this );
}
finally {
this.inferenceBasis = original;
@ -164,17 +165,17 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
}
return null;
},
() -> expression.getFixture().accept( this )
expression.getFixture()
);
SqmExpressibleAccessor<?> resolved = determineCurrentExpressible( expression );
for ( SqmCaseSimple.WhenFragment<?, ?> whenFragment : expression.getWhenFragments() ) {
withTypeInference(
expression.getFixture(),
() -> whenFragment.getCheckValue().accept( this )
whenFragment.getCheckValue()
);
withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
() -> whenFragment.getResult().accept( this )
whenFragment.getResult()
);
resolved = highestPrecedence( resolved, whenFragment.getResult() );
}
@ -182,7 +183,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
if ( expression.getOtherwise() != null ) {
withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
() -> expression.getOtherwise().accept( this )
expression.getOtherwise()
);
}
@ -197,11 +198,11 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
for ( SqmCaseSearched.WhenFragment<?> whenFragment : expression.getWhenFragments() ) {
withTypeInference(
null,
() -> whenFragment.getPredicate().accept( this )
whenFragment.getPredicate()
);
withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
() -> whenFragment.getResult().accept( this )
whenFragment.getResult()
);
resolved = highestPrecedence( resolved, whenFragment.getResult() );
}
@ -209,7 +210,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
if ( expression.getOtherwise() != null ) {
withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
() -> expression.getOtherwise().accept( this )
expression.getOtherwise()
);
}
@ -245,60 +246,56 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
@Override
public Object visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath<?> path) {
path.getLhs().accept( this );
withTypeInference( path.getPluralAttribute().getIndexPathSource(), () -> path.getSelectorExpression().accept( this ) );
withTypeInference( path.getPluralAttribute().getIndexPathSource(), path.getSelectorExpression() );
return path;
}
@Override
public Object visitIsEmptyPredicate(SqmEmptinessPredicate predicate) {
withTypeInference( null, () -> super.visitIsEmptyPredicate( predicate ) );
final SqmExpressibleAccessor<?> original = this.inferenceBasis;
this.inferenceBasis = null;
super.visitIsEmptyPredicate( predicate );
this.inferenceBasis = original;
return predicate;
}
@Override
public Object visitIsNullPredicate(SqmNullnessPredicate predicate) {
withTypeInference( null, () -> super.visitIsNullPredicate( predicate ) );
final SqmExpressibleAccessor<?> original = this.inferenceBasis;
this.inferenceBasis = null;
super.visitIsNullPredicate( predicate );
this.inferenceBasis = original;
return predicate;
}
@Override
public Object visitComparisonPredicate(SqmComparisonPredicate predicate) {
withTypeInference( predicate.getRightHandExpression(), () -> predicate.getLeftHandExpression().accept( this ) );
withTypeInference( predicate.getLeftHandExpression(), () -> predicate.getRightHandExpression().accept( this ) );
withTypeInference( predicate.getRightHandExpression(), predicate.getLeftHandExpression() );
withTypeInference( predicate.getLeftHandExpression(), predicate.getRightHandExpression() );
return predicate;
}
@Override
public Object visitBetweenPredicate(SqmBetweenPredicate predicate) {
withTypeInference( predicate.getLowerBound(), () -> predicate.getExpression().accept( this ) );
withTypeInference(
predicate.getExpression(),
() -> {
predicate.getLowerBound().accept( this );
predicate.getUpperBound().accept( this );
}
);
withTypeInference( predicate.getLowerBound(), predicate.getExpression() );
withTypeInference( predicate.getExpression(), predicate.getLowerBound() );
withTypeInference( predicate.getExpression(), predicate.getUpperBound() );
return predicate;
}
@Override
public Object visitLikePredicate(SqmLikePredicate predicate) {
withTypeInference( predicate.getPattern(), () -> predicate.getMatchExpression().accept( this ) );
withTypeInference(
predicate.getMatchExpression(),
() -> {
predicate.getPattern().accept( this );
withTypeInference( predicate.getPattern(), predicate.getMatchExpression() );
withTypeInference( predicate.getMatchExpression(), predicate.getPattern() );
if ( predicate.getEscapeCharacter() != null ) {
predicate.getEscapeCharacter().accept( this );
withTypeInference( predicate.getMatchExpression(), predicate.getEscapeCharacter() );
}
}
);
return predicate;
}
@Override
public Object visitMemberOfPredicate(SqmMemberOfPredicate predicate) {
withTypeInference( predicate.getPluralPath(), () -> predicate.getLeftHandExpression().accept( this ) );
withTypeInference( predicate.getPluralPath(), predicate.getLeftHandExpression() );
predicate.getPluralPath().accept( this );
return predicate;
}
@ -308,22 +305,17 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
final SqmExpression<?> firstListElement = predicate.getListExpressions().isEmpty()
? null
: predicate.getListExpressions().get( 0 );
withTypeInference( firstListElement, () -> predicate.getTestExpression().accept( this ) );
withTypeInference(
predicate.getTestExpression(),
() -> {
withTypeInference( firstListElement, predicate.getTestExpression() );
for ( SqmExpression<?> expression : predicate.getListExpressions() ) {
expression.accept( this );
withTypeInference( predicate.getTestExpression(), expression );
}
}
);
return predicate;
}
@Override
public Object visitInSubQueryPredicate(SqmInSubQueryPredicate<?> predicate) {
withTypeInference( predicate.getSubQueryExpression(), () -> predicate.getTestExpression().accept( this ) );
withTypeInference( predicate.getTestExpression(), () -> predicate.getSubQueryExpression().accept( this ) );
withTypeInference( predicate.getSubQueryExpression(), predicate.getTestExpression() );
withTypeInference( predicate.getTestExpression(), predicate.getSubQueryExpression() );
return predicate;
}
}