diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java index 7f9f1ea443e5..6ed59353d5f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java @@ -473,7 +473,7 @@ private static CacheableSqmInterpretation buildCacheableSqmInterpretation( final SqlAstTranslator selectTranslator = sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory() - .buildSelectTranslator( sessionFactory, sqmInterpretation.getSqlAst() ); + .buildSelectTranslator( sessionFactory, sqmInterpretation.getSqlAst(), sqmInterpretation.getParameterInfo() ); final var jdbcParamsXref = generateJdbcParamsXref( domainParameterXref, sqmInterpretation::getJdbcParamsBySqmParam ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java index 87a11f9d3dc2..0471be7e75b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java @@ -273,7 +273,7 @@ public static List selectMatchingIds( final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslator sqlAstSelectTranslator = jdbcEnvironment .getSqlAstTranslatorFactory() - .buildSelectTranslator( factory, translation.getSqlAst() ); + .buildSelectTranslator( factory, translation.getSqlAst(), translation.getParameterInfo() ); final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( executionContext.getQueryParameterBindings(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslation.java index ea44a6918d26..2f5d67e247bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslation.java @@ -9,6 +9,7 @@ import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.sql.ast.SqlParameterInfo; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.Statement; @@ -25,4 +26,5 @@ public interface SqmTranslation { FromClauseAccess getFromClauseAccess(); Map, List>> getJdbcParamsBySqmParam(); Map, MappingModelExpressible> getSqmParameterMappingModelTypeResolutions(); + SqlParameterInfo getParameterInfo(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/StandardSqmTranslation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/StandardSqmTranslation.java index 4890f4756189..bfe585ae7322 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/StandardSqmTranslation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/StandardSqmTranslation.java @@ -4,11 +4,15 @@ */ package org.hibernate.query.sqm.sql; +import java.util.Collections; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.sql.ast.SqlParameterInfo; +import org.hibernate.sql.ast.internal.SqlParameterInfoImpl; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.Statement; @@ -24,6 +28,7 @@ public class StandardSqmTranslation implements SqmTranslati private final Map, MappingModelExpressible> parameterMappingModelTypeMap; private final SqlExpressionResolver sqlExpressionResolver; private final FromClauseAccess fromClauseAccess; + private final SqlParameterInfo parameterInfo; public StandardSqmTranslation( T sqlAst, @@ -36,6 +41,19 @@ public StandardSqmTranslation( this.parameterMappingModelTypeMap = parameterMappingModelTypeMap; this.sqlExpressionResolver = sqlExpressionResolver; this.fromClauseAccess = fromClauseAccess; + final IdentityHashMap parameterIdMap = new IdentityHashMap<>(); + int parameterId = 0; + for ( Map.Entry, List>> entry : jdbcParamMap.entrySet() ) { + final List> parameterUses = entry.getValue(); + final int baseId = parameterId; + for ( List jdbcParameters : parameterUses ) { + for ( int i = 0; i < jdbcParameters.size(); i++ ) { + parameterIdMap.put( jdbcParameters.get( i ), baseId + i ); + } + } + parameterId += parameterUses.get( 0 ).size(); + } + this.parameterInfo = new SqlParameterInfoImpl( Collections.unmodifiableMap( parameterIdMap ) ); } @Override @@ -62,4 +80,9 @@ public SqlExpressionResolver getSqlExpressionResolver() { public FromClauseAccess getFromClauseAccess() { return fromClauseAccess; } + + @Override + public SqlParameterInfo getParameterInfo() { + return parameterInfo; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java index 91f35924bdc1..ec647fd9deb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java @@ -23,6 +23,11 @@ public interface SqlAstTranslatorFactory { */ SqlAstTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory, SelectStatement statement); + /** + * Builds a single-use select translator + */ + SqlAstTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory, SelectStatement statement, SqlParameterInfo parameterInfo); + /** * Builds a single-use mutation translator */ diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlParameterInfo.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlParameterInfo.java new file mode 100644 index 000000000000..24da78b35cde --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlParameterInfo.java @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.sql.ast; + +import org.hibernate.sql.ast.tree.expression.JdbcParameter; + +/** + * @since 7.0 + */ +public interface SqlParameterInfo { + int getParameterId(JdbcParameter jdbcParameter); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/SqlParameterInfoImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/SqlParameterInfoImpl.java new file mode 100644 index 000000000000..92700a2abfcb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/internal/SqlParameterInfoImpl.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.sql.ast.internal; + +import org.hibernate.sql.ast.SqlParameterInfo; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; + +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * @since 7.0 + */ +public class SqlParameterInfoImpl implements SqlParameterInfo { + + private final Map parameterIdMap; + + public SqlParameterInfoImpl() { + this(new IdentityHashMap<>()); + } + + public SqlParameterInfoImpl(Map parameterIdMap) { + this.parameterIdMap = parameterIdMap; + } + + @Override + public int getParameterId(JdbcParameter jdbcParameter) { + final Integer id = parameterIdMap.get( jdbcParameter ); + if ( id == null ) { + final int newId = parameterIdMap.size(); + parameterIdMap.put( jdbcParameter, newId ); + return newId; + } + return id; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index e220e9bf2cb6..fbacfbe29200 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -22,6 +22,7 @@ import java.util.function.Function; import java.util.function.Supplier; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.AssertionFailure; import org.hibernate.Internal; import org.hibernate.LockMode; @@ -87,7 +88,9 @@ import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.SqlParameterInfo; import org.hibernate.sql.ast.SqlTreeCreationException; +import org.hibernate.sql.ast.internal.SqlParameterInfoImpl; import org.hibernate.sql.ast.internal.TableGroupHelper; import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard; import org.hibernate.sql.ast.tree.AbstractUpdateOrDeleteStatement; @@ -311,6 +314,7 @@ public abstract class AbstractSqlAstTranslator implemen private final Stack clauseStack = new StandardStack<>(); private final Stack queryPartStack = new StandardStack<>(); private final Stack statementStack = new StandardStack<>(); + private @Nullable SqlParameterInfo parameterInfo; private final Dialect dialect; private final Set affectedTableNames = new HashSet<>(); @@ -345,11 +349,16 @@ public abstract class AbstractSqlAstTranslator implemen private ForUpdateClause forUpdate; protected AbstractSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { + this( sessionFactory, statement, null ); + } + + protected AbstractSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement, @Nullable SqlParameterInfo parameterInfo) { this.sessionFactory = sessionFactory; final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); this.dialect = jdbcServices.getDialect(); this.statementStack.push( statement ); this.parameterMarkerStrategy = jdbcServices.getParameterMarkerStrategy(); + this.parameterInfo = parameterInfo; } private static Clause matchWithClause(Clause clause) { @@ -368,6 +377,13 @@ public SessionFactoryImplementor getSessionFactory() { return sessionFactory; } + protected SqlParameterInfo getParameterInfo() { + if ( parameterInfo == null ) { + parameterInfo = new SqlParameterInfoImpl(); + } + return parameterInfo; + } + protected FunctionRenderer castFunction() { if ( castFunction == null ) { castFunction = findSelfRenderingFunction( "cast", 2 ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslator.java index 21b792d8a680..833554120f31 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslator.java @@ -4,7 +4,9 @@ */ package org.hibernate.sql.ast.spi; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.sql.ast.SqlParameterInfo; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; @@ -21,4 +23,8 @@ public class StandardSqlAstTranslator extends AbstractS public StandardSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { super( sessionFactory, statement ); } + + public StandardSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement, @Nullable SqlParameterInfo parameterInfo) { + super( sessionFactory, statement, parameterInfo ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java index 8bedd80c6213..fb89a1b30922 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java @@ -4,9 +4,11 @@ */ package org.hibernate.sql.ast.spi; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.SqlParameterInfo; import org.hibernate.sql.ast.tree.MutationStatement; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.select.SelectStatement; @@ -22,20 +24,24 @@ * @author Steve Ebersole */ public class StandardSqlAstTranslatorFactory implements SqlAstTranslatorFactory { - @Override public SqlAstTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory, SelectStatement statement) { - return buildTranslator( sessionFactory, statement ); + return buildTranslator( sessionFactory, statement, null ); + } + + @Override + public SqlAstTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory, SelectStatement statement, SqlParameterInfo parameterInfo) { + return buildTranslator( sessionFactory, statement, parameterInfo ); } @Override public SqlAstTranslator buildMutationTranslator(SessionFactoryImplementor sessionFactory, MutationStatement statement) { - return buildTranslator( sessionFactory, statement ); + return buildTranslator( sessionFactory, statement, null ); } @Override public SqlAstTranslator buildModelMutationTranslator(TableMutation mutation, SessionFactoryImplementor sessionFactory) { - return buildTranslator( sessionFactory, mutation ); + return buildTranslator( sessionFactory, mutation, null ); } /** @@ -45,4 +51,11 @@ protected SqlAstTranslator buildTranslator(SessionF return new StandardSqlAstTranslator<>( sessionFactory, statement ); } + /** + * Consolidated building of a translator for all Query cases + */ + protected SqlAstTranslator buildTranslator(SessionFactoryImplementor sessionFactory, Statement statement, @Nullable SqlParameterInfo parameterInfo) { + return buildTranslator( sessionFactory, statement ); + } + }