HHH-16051 Avoid stateful lambdas on invocations of Stack#findCurrentFirst

This commit is contained in:
Sanne Grinovero 2023-01-16 18:20:36 +00:00 committed by Sanne Grinovero
parent c570b11dcd
commit 84a652bfe7
8 changed files with 142 additions and 110 deletions

View File

@ -147,7 +147,11 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
}
private boolean isPartOfQueryGroup() {
return getQueryPartStack().findCurrentFirst( part -> part instanceof QueryGroup ? part : null ) != null;
return getQueryPartStack().findCurrentFirst( OracleSqlAstTranslator::partIsQueryGroup ) != null;
}
private static QueryPart partIsQueryGroup(final QueryPart part) {
return part instanceof QueryGroup ? part : null;
}
@Override

View File

@ -6,9 +6,12 @@
*/
package org.hibernate.internal.util.collections;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import org.hibernate.Incubating;
/**
* Stack implementation exposing useful methods for Hibernate needs.
*
@ -62,4 +65,21 @@ public interface Stack<T> {
* returned from `action` stops the iteration and is returned from here
*/
<X> X findCurrentFirst(Function<T, X> action);
/**
* Runs a function on each couple defined as {Y,T} in which Y
* is fixed, and T is each instance of this stack.
* Not all Ts might be presented as the iteration is interrupted as
* soon as the first non-null value is returned by the (bi)function,
* which is then returned from this function.
* @param parameter a parameter to be passed to the (bi)funtion
* @param biFunction a (bi)function taking as input parameter Y and each of the T
* from the current stack, and return type of X.
* @return the first non-null result by the function, or null if no matches.
* @param <X> the return type of the function
* @param <Y> the type of the fixed parameter
*/
@Incubating
<X,Y> X findCurrentFirstWithParameter(Y parameter, BiFunction<T, Y, X> biFunction);
}

View File

@ -9,6 +9,7 @@ package org.hibernate.internal.util.collections;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@ -126,4 +127,18 @@ public final class StandardStack<T> implements Stack<T> {
return null;
}
@Override
public <X,Y> X findCurrentFirstWithParameter(Y parameter, BiFunction<T, Y, X> biFunction) {
if ( internalStack == null ) {
return null;
}
for ( T t : internalStack ) {
final X result = biFunction.apply( t, parameter );
if ( result != null ) {
return result;
}
}
return null;
}
}

View File

@ -19,6 +19,7 @@ import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.criteria.JpaCteCriteria;
import org.hibernate.query.criteria.JpaCteCriteriaAttribute;
import org.hibernate.query.criteria.JpaSearchOrder;
import org.hibernate.query.sqm.tree.SqmQuery;
import org.hibernate.query.sqm.tree.cte.SqmCteTableColumn;
import org.hibernate.query.sqm.tree.cte.SqmSearchClauseSpecification;
import org.hibernate.query.sqm.tree.domain.SqmCteRoot;
@ -339,16 +340,17 @@ public class QuerySplitter {
}
@Override
public SqmCteStatement<?> findCteStatement(String name) {
return processingStateStack.findCurrentFirst(
state -> {
if ( state.getProcessingQuery() instanceof SqmCteContainer ) {
final SqmCteContainer container = (SqmCteContainer) state.getProcessingQuery();
return container.getCteStatement( name );
}
return null;
}
);
public SqmCteStatement<?> findCteStatement(final String name) {
return processingStateStack.findCurrentFirstWithParameter( name, UnmappedPolymorphismReplacer::matchCteStatement );
}
private static SqmCteStatement<?> matchCteStatement(final SqmCreationProcessingState state, final String cteName) {
final SqmQuery<?> processingQuery = state.getProcessingQuery();
if ( processingQuery instanceof SqmCteContainer ) {
final SqmCteContainer container = (SqmCteContainer) processingQuery;
return container.getCteStatement( cteName );
}
return null;
}
@Override

View File

@ -1944,15 +1944,15 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( currentPotentialRecursiveCte != null && name.equals( currentPotentialRecursiveCte.getName() ) ) {
return (SqmCteStatement<?>) currentPotentialRecursiveCte;
}
return processingStateStack.findCurrentFirst(
state -> {
if ( state.getProcessingQuery() instanceof SqmCteContainer ) {
final SqmCteContainer container = (SqmCteContainer) state.getProcessingQuery();
return container.getCteStatement( name );
}
return null;
}
);
return processingStateStack.findCurrentFirstWithParameter( name, SemanticQueryBuilder::matchCteStatement );
}
private static SqmCteStatement<?> matchCteStatement(SqmCreationProcessingState state, String n) {
if ( state.getProcessingQuery() instanceof SqmCteContainer ) {
final SqmCteContainer container = (SqmCteContainer) state.getProcessingQuery();
return container.getCteStatement( n );
}
return null;
}
@Override

View File

@ -573,6 +573,31 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
.getJpaCriteriaParamResolutions();
}
private static Boolean stackMatchHelper(SqlAstProcessingState processingState, SqlAstProcessingState c) {
if ( !( processingState instanceof SqlAstQueryPartProcessingState ) ) {
return Boolean.FALSE;
}
if ( processingState == c ) {
return null;
}
final QueryPart part = ( (SqlAstQueryPartProcessingState) processingState ).getInflightQueryPart();
if ( part instanceof QueryGroup ) {
if ( ( (QueryGroup) part ).getQueryParts().isEmpty() ) {
return null;
}
}
return Boolean.FALSE;
}
private static Boolean matchSqlAstWithQueryPart(SqlAstProcessingState state, QueryPart cteQueryPartLocal) {
if ( state instanceof SqlAstQueryPartProcessingState ) {
if ( ( (SqlAstQueryPartProcessingState) state ).getInflightQueryPart() == cteQueryPartLocal ) {
return Boolean.TRUE;
}
}
return null;
}
public Map<SqmParameter<?>, MappingModelExpressible<?>> getSqmParameterMappingModelExpressibleResolutions() {
return sqmParameterMappingModelTypes;
}
@ -2226,23 +2251,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
// Since we only want to create domain results for the first/left-most query spec within query groups,
// we have to check if the current query spec is the left-most.
// This is the case when all upper level in-flight query groups are still empty
collectDomainResults = needsDomainResults && processingStateStack.findCurrentFirst(
processingState -> {
if ( !( processingState instanceof SqlAstQueryPartProcessingState ) ) {
return Boolean.FALSE;
}
if ( processingState == current ) {
return null;
}
final QueryPart part = ( (SqlAstQueryPartProcessingState) processingState ).getInflightQueryPart();
if ( part instanceof QueryGroup ) {
if ( ( (QueryGroup) part ).getQueryParts().isEmpty() ) {
return null;
}
}
return Boolean.FALSE;
}
) == null;
collectDomainResults = needsDomainResults && processingStateStack.findCurrentFirstWithParameter( current, BaseSqmToSqlAstConverter::stackMatchHelper ) == null;
}
// this `currentSqlSelectionCollector().next()` is meant solely for resolving
// literal reference to a selection-item in the order-by or group-by clause.
@ -2825,16 +2834,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
final QueryPart cteQueryPart = ( (SelectStatement) cteStatement.getCteDefinition() ).getQueryPart();
// If the query part of the CTE is one which we are currently processing, then this is a recursive CTE
if ( cteQueryPart instanceof QueryGroup && Boolean.TRUE == processingStateStack.findCurrentFirst(
state -> {
if ( state instanceof SqlAstQueryPartProcessingState ) {
if ( ( (SqlAstQueryPartProcessingState) state ).getInflightQueryPart() == cteQueryPart ) {
return Boolean.TRUE;
}
}
return null;
}
) ) {
if ( cteQueryPart instanceof QueryGroup && Boolean.TRUE == processingStateStack.findCurrentFirstWithParameter( cteQueryPart, BaseSqmToSqlAstConverter::matchSqlAstWithQueryPart ) ) {
cteStatement.setRecursive();
}
final AnonymousTupleTableGroupProducer tableGroupProducer = cteStatement.getCteTable().getTableGroupProducer();

View File

@ -310,6 +310,13 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
private JdbcParameter limitParameter;
private ForUpdateClause forUpdate;
private static Clause matchWithClause(Clause clause) {
if ( clause == Clause.WITH ) {
return Clause.WITH;
}
return null;
}
public Dialect getDialect() {
return dialect;
}
@ -470,14 +477,14 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
public MutationStatement getCurrentDmlStatement() {
return statementStack.findCurrentFirst(
stmt -> {
if ( stmt instanceof MutationStatement && !( stmt instanceof InsertSelectStatement ) ) {
return (MutationStatement) stmt;
}
return null;
}
);
return statementStack.findCurrentFirst( AbstractSqlAstTranslator::matchMutationStatementNoInsertSelect );
}
private static MutationStatement matchMutationStatementNoInsertSelect(Statement stmt) {
if ( stmt instanceof MutationStatement && !( stmt instanceof InsertSelectStatement ) ) {
return (MutationStatement) stmt;
}
return null;
}
protected SqlAstNodeRenderingMode getParameterRenderingMode() {
@ -688,16 +695,17 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
protected boolean inOverOrWithinGroupClause() {
return clauseStack.findCurrentFirst(
clause -> {
switch ( clause ) {
case OVER:
case WITHIN_GROUP:
return true;
}
return null;
}
) != null;
return clauseStack.findCurrentFirst( AbstractSqlAstTranslator::matchOverOrWithinGroupClauses ) != null;
}
private static Boolean matchOverOrWithinGroupClauses(final Clause clause) {
switch ( clause ) {
case OVER:
case WITHIN_GROUP:
return Boolean.TRUE;
default:
return null;
}
}
protected Stack<Clause> getClauseStack() {
@ -722,15 +730,26 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
return currentCteStatement;
}
protected CteStatement getCteStatement(String cteName) {
return statementStack.findCurrentFirst(
stmt -> {
if ( stmt instanceof CteContainer ) {
return ( (CteContainer) stmt ).getCteStatement( cteName );
}
return null;
}
);
protected CteStatement getCteStatement(final String cteName) {
return statementStack.findCurrentFirstWithParameter( cteName, AbstractSqlAstTranslator::matchCteStatement );
}
private static CteStatement matchCteStatement(final Statement stmt, final String cteName) {
if ( stmt instanceof CteContainer ) {
final CteContainer cteContainer = (CteContainer) stmt;
return cteContainer.getCteStatement( cteName );
}
return null;
}
private static CteContainer matchCteContainerByStatement(final Statement stmt, final String cteName) {
if ( stmt instanceof CteContainer ) {
final CteContainer cteContainer = (CteContainer) stmt;
if ( cteContainer.getCteStatement( cteName ) != null ) {
return cteContainer;
}
}
return null;
}
@Override
@ -1508,14 +1527,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
pushToTopLevel = !supportsNestedWithClause()
|| !supportsWithClauseInSubquery() && isInSubquery();
}
final boolean inNestedWithClause = clauseStack.findCurrentFirst(
clause -> {
if ( clause == Clause.WITH ) {
return Clause.WITH;
}
return null;
}
) != null;
final boolean inNestedWithClause = clauseStack.findCurrentFirst( AbstractSqlAstTranslator::matchWithClause ) != null;
clauseStack.push( Clause.WITH );
if ( !pushToTopLevel ) {
appendSql( "with " );
@ -1796,17 +1808,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
if ( !supportsWithClauseInSubquery() && isInSubquery() ) {
final String cteName = tableGroup.getPrimaryTableReference().getTableId();
final CteContainer cteOwner = statementStack.findCurrentFirst(
stmt -> {
if ( stmt instanceof CteContainer ) {
final CteContainer cteContainer = (CteContainer) stmt;
if ( cteContainer.getCteStatement( cteName ) != null ) {
return cteContainer;
}
}
return null;
}
);
final CteContainer cteOwner = statementStack.findCurrentFirstWithParameter( cteName, AbstractSqlAstTranslator::matchCteContainerByStatement );
// If the CTE is owned by the root statement, it will be rendered as CTE, so we can refer to it
return cteOwner != statementStack.getRoot() && !cteOwner.getCteStatement( cteName ).isRecursive();
}

View File

@ -48,22 +48,16 @@ public class LoadContexts {
}
}
public LoadingEntityEntry findLoadingEntityEntry(EntityKey entityKey) {
return jdbcValuesSourceProcessingStateStack.findCurrentFirst(
state -> state.findLoadingEntityLocally( entityKey )
);
public LoadingEntityEntry findLoadingEntityEntry(final EntityKey entityKey) {
return jdbcValuesSourceProcessingStateStack.findCurrentFirstWithParameter( entityKey, JdbcValuesSourceProcessingState::findLoadingEntityLocally );
}
public LoadingCollectionEntry findLoadingCollectionEntry(CollectionKey collectionKey) {
return jdbcValuesSourceProcessingStateStack.findCurrentFirst(
state -> state.findLoadingCollectionLocally( collectionKey )
);
public LoadingCollectionEntry findLoadingCollectionEntry(final CollectionKey collectionKey) {
return jdbcValuesSourceProcessingStateStack.findCurrentFirstWithParameter( collectionKey, JdbcValuesSourceProcessingState::findLoadingCollectionLocally );
}
public Initializer findInitializer(EntityUniqueKey key){
return jdbcValuesSourceProcessingStateStack.findCurrentFirst(
state -> state.findInitializer( key )
);
public Initializer findInitializer(final EntityUniqueKey key){
return jdbcValuesSourceProcessingStateStack.findCurrentFirstWithParameter( key, JdbcValuesSourceProcessingState::findInitializer );
}
/**
@ -75,11 +69,6 @@ public class LoadContexts {
return persistenceContext;
}
private SharedSessionContractImplementor getSession() {
return getPersistenceContext().getSession();
}
/**
* Release internal state associated with *all* result sets.
* <p>