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.AnyDiscriminatorSqmPath;
import org.hibernate.metamodel.model.domain.internal.EntityDiscriminatorSqmPath; 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.SemanticQueryWalker;
import org.hibernate.query.sqm.tree.SqmVisitableNode; import org.hibernate.query.sqm.tree.SqmVisitableNode;
import org.hibernate.query.sqm.tree.cte.SqmCteContainer; 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.SqmCteJoin;
import org.hibernate.query.sqm.tree.from.SqmDerivedJoin; import org.hibernate.query.sqm.tree.from.SqmDerivedJoin;
import org.hibernate.query.sqm.tree.from.SqmEntityJoin; 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.SqmFromClause;
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.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; 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.SqmAssignment;
import org.hibernate.query.sqm.tree.update.SqmSetClause; import org.hibernate.query.sqm.tree.update.SqmSetClause;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; 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; import org.hibernate.service.ServiceRegistry;
/** /**
@ -247,84 +252,166 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
@Override @Override
public Object visitFromClause(SqmFromClause fromClause) { public Object visitFromClause(SqmFromClause fromClause) {
fromClause.visitRoots( root -> root.accept( this ) ); fromClause.visitRoots( this::consumeFromClauseRoot );
return fromClause; 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 @Override
public Object visitRootPath(SqmRoot<?> sqmRoot) { public Object visitRootPath(SqmRoot<?> sqmRoot) {
sqmRoot.visitReusablePaths( path -> path.accept( this ) );
sqmRoot.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return sqmRoot; return sqmRoot;
} }
@Override @Override
public Object visitRootDerived(SqmDerivedRoot<?> sqmRoot) { public Object visitRootDerived(SqmDerivedRoot<?> sqmRoot) {
sqmRoot.getQueryPart().accept( this );
sqmRoot.visitReusablePaths( path -> path.accept( this ) );
sqmRoot.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return sqmRoot; return sqmRoot;
} }
@Override @Override
public Object visitRootCte(SqmCteRoot<?> sqmRoot) { public Object visitRootCte(SqmCteRoot<?> sqmRoot) {
sqmRoot.visitReusablePaths( path -> path.accept( this ) );
sqmRoot.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return sqmRoot; return sqmRoot;
} }
@Override @Override
public Object visitCrossJoin(SqmCrossJoin<?> joinedFromElement) { public Object visitCrossJoin(SqmCrossJoin<?> joinedFromElement) {
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );
joinedFromElement.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return joinedFromElement; return joinedFromElement;
} }
@Override @Override
public Object visitPluralPartJoin(SqmPluralPartJoin<?, ?> joinedFromElement) { public Object visitPluralPartJoin(SqmPluralPartJoin<?, ?> joinedFromElement) {
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );
joinedFromElement.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return joinedFromElement; return joinedFromElement;
} }
@Override @Override
public Object visitQualifiedEntityJoin(SqmEntityJoin<?> joinedFromElement) { 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; return joinedFromElement;
} }
@Override @Override
public Object visitQualifiedAttributeJoin(SqmAttributeJoin<?,?> joinedFromElement) { 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; return joinedFromElement;
} }
@Override @Override
public Object visitQualifiedDerivedJoin(SqmDerivedJoin<?> joinedFromElement) { 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; return joinedFromElement;
} }
@Override @Override
public Object visitQualifiedCteJoin(SqmCteJoin<?> joinedFromElement) { 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; return joinedFromElement;
} }
@ -385,8 +472,6 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
@Override @Override
public Object visitCorrelation(SqmCorrelation<?, ?> correlation) { public Object visitCorrelation(SqmCorrelation<?, ?> correlation) {
correlation.visitReusablePaths( path -> path.accept( this ) );
correlation.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return correlation; 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.spi.BaseSemanticQueryWalker;
import org.hibernate.query.sqm.tree.SqmExpressibleAccessor; import org.hibernate.query.sqm.tree.SqmExpressibleAccessor;
import org.hibernate.query.sqm.tree.SqmStatement; 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.domain.SqmIndexedCollectionAccessPath;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
@ -98,7 +99,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
@Override @Override
public Object visitFunction(SqmFunction<?> sqmFunction) { public Object visitFunction(SqmFunction<?> sqmFunction) {
SqmExpressibleAccessor<?> current = inferenceBasis; final SqmExpressibleAccessor<?> current = inferenceBasis;
inferenceBasis = null; inferenceBasis = null;
super.visitFunction( sqmFunction ); super.visitFunction( sqmFunction );
inferenceBasis = current; inferenceBasis = current;
@ -140,11 +141,11 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
private SqmExpressibleAccessor<?> inferenceBasis; private SqmExpressibleAccessor<?> inferenceBasis;
private void withTypeInference(SqmExpressibleAccessor<?> inferenceBasis, Runnable action) { private void withTypeInference(SqmExpressibleAccessor<?> inferenceBasis, SqmVisitableNode sqmVisitableNode) {
SqmExpressibleAccessor<?> original = this.inferenceBasis; SqmExpressibleAccessor<?> original = this.inferenceBasis;
this.inferenceBasis = inferenceBasis; this.inferenceBasis = inferenceBasis;
try { try {
action.run(); sqmVisitableNode.accept( this );
} }
finally { finally {
this.inferenceBasis = original; this.inferenceBasis = original;
@ -164,17 +165,17 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
} }
return null; return null;
}, },
() -> expression.getFixture().accept( this ) expression.getFixture()
); );
SqmExpressibleAccessor<?> resolved = determineCurrentExpressible( expression ); SqmExpressibleAccessor<?> resolved = determineCurrentExpressible( expression );
for ( SqmCaseSimple.WhenFragment<?, ?> whenFragment : expression.getWhenFragments() ) { for ( SqmCaseSimple.WhenFragment<?, ?> whenFragment : expression.getWhenFragments() ) {
withTypeInference( withTypeInference(
expression.getFixture(), expression.getFixture(),
() -> whenFragment.getCheckValue().accept( this ) whenFragment.getCheckValue()
); );
withTypeInference( withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved, resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
() -> whenFragment.getResult().accept( this ) whenFragment.getResult()
); );
resolved = highestPrecedence( resolved, whenFragment.getResult() ); resolved = highestPrecedence( resolved, whenFragment.getResult() );
} }
@ -182,7 +183,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
if ( expression.getOtherwise() != null ) { if ( expression.getOtherwise() != null ) {
withTypeInference( withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved, 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() ) { for ( SqmCaseSearched.WhenFragment<?> whenFragment : expression.getWhenFragments() ) {
withTypeInference( withTypeInference(
null, null,
() -> whenFragment.getPredicate().accept( this ) whenFragment.getPredicate()
); );
withTypeInference( withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved, resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
() -> whenFragment.getResult().accept( this ) whenFragment.getResult()
); );
resolved = highestPrecedence( resolved, whenFragment.getResult() ); resolved = highestPrecedence( resolved, whenFragment.getResult() );
} }
@ -209,7 +210,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
if ( expression.getOtherwise() != null ) { if ( expression.getOtherwise() != null ) {
withTypeInference( withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved, resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
() -> expression.getOtherwise().accept( this ) expression.getOtherwise()
); );
} }
@ -245,60 +246,56 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
@Override @Override
public Object visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath<?> path) { public Object visitIndexedPluralAccessPath(SqmIndexedCollectionAccessPath<?> path) {
path.getLhs().accept( this ); path.getLhs().accept( this );
withTypeInference( path.getPluralAttribute().getIndexPathSource(), () -> path.getSelectorExpression().accept( this ) ); withTypeInference( path.getPluralAttribute().getIndexPathSource(), path.getSelectorExpression() );
return path; return path;
} }
@Override @Override
public Object visitIsEmptyPredicate(SqmEmptinessPredicate predicate) { 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; return predicate;
} }
@Override @Override
public Object visitIsNullPredicate(SqmNullnessPredicate predicate) { 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; return predicate;
} }
@Override @Override
public Object visitComparisonPredicate(SqmComparisonPredicate predicate) { public Object visitComparisonPredicate(SqmComparisonPredicate predicate) {
withTypeInference( predicate.getRightHandExpression(), () -> predicate.getLeftHandExpression().accept( this ) ); withTypeInference( predicate.getRightHandExpression(), predicate.getLeftHandExpression() );
withTypeInference( predicate.getLeftHandExpression(), () -> predicate.getRightHandExpression().accept( this ) ); withTypeInference( predicate.getLeftHandExpression(), predicate.getRightHandExpression() );
return predicate; return predicate;
} }
@Override @Override
public Object visitBetweenPredicate(SqmBetweenPredicate predicate) { public Object visitBetweenPredicate(SqmBetweenPredicate predicate) {
withTypeInference( predicate.getLowerBound(), () -> predicate.getExpression().accept( this ) ); withTypeInference( predicate.getLowerBound(), predicate.getExpression() );
withTypeInference( withTypeInference( predicate.getExpression(), predicate.getLowerBound() );
predicate.getExpression(), withTypeInference( predicate.getExpression(), predicate.getUpperBound() );
() -> {
predicate.getLowerBound().accept( this );
predicate.getUpperBound().accept( this );
}
);
return predicate; return predicate;
} }
@Override @Override
public Object visitLikePredicate(SqmLikePredicate predicate) { public Object visitLikePredicate(SqmLikePredicate predicate) {
withTypeInference( predicate.getPattern(), () -> predicate.getMatchExpression().accept( this ) ); withTypeInference( predicate.getPattern(), predicate.getMatchExpression() );
withTypeInference( withTypeInference( predicate.getMatchExpression(), predicate.getPattern() );
predicate.getMatchExpression(), if ( predicate.getEscapeCharacter() != null ) {
() -> { withTypeInference( predicate.getMatchExpression(), predicate.getEscapeCharacter() );
predicate.getPattern().accept( this ); }
if ( predicate.getEscapeCharacter() != null ) {
predicate.getEscapeCharacter().accept( this );
}
}
);
return predicate; return predicate;
} }
@Override @Override
public Object visitMemberOfPredicate(SqmMemberOfPredicate predicate) { public Object visitMemberOfPredicate(SqmMemberOfPredicate predicate) {
withTypeInference( predicate.getPluralPath(), () -> predicate.getLeftHandExpression().accept( this ) ); withTypeInference( predicate.getPluralPath(), predicate.getLeftHandExpression() );
predicate.getPluralPath().accept( this ); predicate.getPluralPath().accept( this );
return predicate; return predicate;
} }
@ -308,22 +305,17 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
final SqmExpression<?> firstListElement = predicate.getListExpressions().isEmpty() final SqmExpression<?> firstListElement = predicate.getListExpressions().isEmpty()
? null ? null
: predicate.getListExpressions().get( 0 ); : predicate.getListExpressions().get( 0 );
withTypeInference( firstListElement, () -> predicate.getTestExpression().accept( this ) ); withTypeInference( firstListElement, predicate.getTestExpression() );
withTypeInference( for ( SqmExpression<?> expression : predicate.getListExpressions() ) {
predicate.getTestExpression(), withTypeInference( predicate.getTestExpression(), expression );
() -> { }
for ( SqmExpression<?> expression : predicate.getListExpressions() ) {
expression.accept( this );
}
}
);
return predicate; return predicate;
} }
@Override @Override
public Object visitInSubQueryPredicate(SqmInSubQueryPredicate<?> predicate) { public Object visitInSubQueryPredicate(SqmInSubQueryPredicate<?> predicate) {
withTypeInference( predicate.getSubQueryExpression(), () -> predicate.getTestExpression().accept( this ) ); withTypeInference( predicate.getSubQueryExpression(), predicate.getTestExpression() );
withTypeInference( predicate.getTestExpression(), () -> predicate.getSubQueryExpression().accept( this ) ); withTypeInference( predicate.getTestExpression(), predicate.getSubQueryExpression() );
return predicate; return predicate;
} }
} }