Skip to content

Commit 97176b1

Browse files
beikovsebersole
authored andcommitted
HHH-19632 Introduce parameter id concept to allow deduplication with native parameter markers
1 parent 9e5945f commit 97176b1

File tree

8 files changed

+130
-61
lines changed

8 files changed

+130
-61
lines changed

hibernate-core/src/main/java/org/hibernate/internal/FilterJdbcParameter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.sql.PreparedStatement;
88
import java.sql.SQLException;
99

10+
import org.checkerframework.checker.nullness.qual.Nullable;
1011
import org.hibernate.metamodel.mapping.JdbcMapping;
1112
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
1213
import org.hibernate.sql.ast.SqlAstWalker;
@@ -48,6 +49,11 @@ public JdbcMappingContainer getExpressionType() {
4849
return jdbcMapping;
4950
}
5051

52+
@Override
53+
public @Nullable Integer getParameterId() {
54+
return null;
55+
}
56+
5157
@Override
5258
public void accept(SqlAstWalker sqlTreeWalker) {
5359
throw new IllegalStateException( );

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.hibernate.id.CompositeNestedGeneratedValueGenerator;
2727
import org.hibernate.id.OptimizableGenerator;
2828
import org.hibernate.id.enhanced.Optimizer;
29+
import org.hibernate.internal.util.NullnessUtil;
2930
import org.hibernate.internal.util.collections.Stack;
3031
import org.hibernate.internal.util.collections.StandardStack;
3132
import org.hibernate.loader.MultipleBagFetchException;
@@ -6197,14 +6198,17 @@ private void resolveSqmParameter(
61976198
MappingModelExpressible<?> valueMapping,
61986199
BiConsumer<Integer,JdbcParameter> jdbcParameterConsumer) {
61996200
sqmParameterMappingModelTypes.put( expression, valueMapping );
6201+
final List<List<JdbcParameter>> jdbcParams = jdbcParamsBySqmParam.get( expression );
6202+
final int parameterId = jdbcParams == null ? jdbcParamsBySqmParam.size()
6203+
: NullnessUtil.castNonNull( jdbcParams.get( 0 ).get( 0 ).getParameterId() );
62006204
final Bindable bindable = bindable( valueMapping );
62016205
if ( bindable instanceof SelectableMappings selectableMappings ) {
62026206
selectableMappings.forEachSelectable(
6203-
(index, selectableMapping) -> jdbcParameterConsumer.accept( index, new SqlTypedMappingJdbcParameter( selectableMapping ) )
6207+
(index, selectableMapping) -> jdbcParameterConsumer.accept( index, new SqlTypedMappingJdbcParameter( selectableMapping, parameterId ) )
62046208
);
62056209
}
62066210
else if ( bindable instanceof SelectableMapping selectableMapping ) {
6207-
jdbcParameterConsumer.accept( 0, new SqlTypedMappingJdbcParameter( selectableMapping ) );
6211+
jdbcParameterConsumer.accept( 0, new SqlTypedMappingJdbcParameter( selectableMapping, parameterId ) );
62086212
}
62096213
else {
62106214
final SqlTypedMapping sqlTypedMapping = sqlTypedMapping( expression, bindable );
@@ -6216,12 +6220,12 @@ else if ( bindable instanceof SelectableMapping selectableMapping ) {
62166220
bindable.forEachJdbcType(
62176221
(index, jdbcMapping) -> jdbcParameterConsumer.accept(
62186222
index,
6219-
new JdbcParameterImpl( jdbcMapping )
6223+
new JdbcParameterImpl( jdbcMapping, parameterId )
62206224
)
62216225
);
62226226
}
62236227
else {
6224-
jdbcParameterConsumer.accept( 0, new SqlTypedMappingJdbcParameter( sqlTypedMapping ) );
6228+
jdbcParameterConsumer.accept( 0, new SqlTypedMappingJdbcParameter( sqlTypedMapping, parameterId ) );
62256229
}
62266230
}
62276231
}

hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java

Lines changed: 74 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@
7070
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
7171
import org.hibernate.sql.ast.SqlAstTranslator;
7272
import org.hibernate.sql.ast.SqlTreeCreationException;
73-
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
7473
import org.hibernate.sql.ast.internal.TableGroupHelper;
74+
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
7575
import org.hibernate.sql.ast.tree.AbstractUpdateOrDeleteStatement;
7676
import org.hibernate.sql.ast.tree.MutationStatement;
7777
import org.hibernate.sql.ast.tree.SqlAstNode;
@@ -135,7 +135,6 @@
135135
import org.hibernate.sql.exec.internal.AbstractJdbcParameter;
136136
import org.hibernate.sql.exec.internal.JdbcOperationQueryInsertImpl;
137137
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
138-
import org.hibernate.sql.exec.internal.JdbcParametersImpl;
139138
import org.hibernate.sql.exec.internal.SqlTypedMappingJdbcParameter;
140139
import org.hibernate.sql.exec.spi.ExecutionContext;
141140
import org.hibernate.sql.exec.spi.JdbcLockStrategy;
@@ -177,6 +176,7 @@
177176
import java.sql.SQLException;
178177
import java.time.Period;
179178
import java.util.ArrayList;
179+
import java.util.Arrays;
180180
import java.util.BitSet;
181181
import java.util.Collection;
182182
import java.util.Collections;
@@ -260,7 +260,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
260260
private final StringBuilder sqlBuffer = new StringBuilder();
261261

262262
private final List<JdbcParameterBinder> parameterBinders = new ArrayList<>();
263-
private final JdbcParametersImpl jdbcParameters = new JdbcParametersImpl();
263+
private int[] parameterIdToBinderIndex;
264264
private JdbcParameterBindings jdbcParameterBindings;
265265
private Map<JdbcParameter, JdbcParameterBinding> appliedParameterBindings = Collections.emptyMap();
266266
private SqlAstNodeRenderingMode parameterRenderingMode = SqlAstNodeRenderingMode.DEFAULT;
@@ -4607,11 +4607,10 @@ protected void renderFetchPlusOffsetExpressionAsSingleParameter(
46074607
appendSql( fetchCount.intValue() + offsetCount.intValue() + offset );
46084608
}
46094609
else {
4610-
appendSql( PARAM_MARKER );
46114610
final JdbcParameter offsetParameter = (JdbcParameter) offsetClauseExpression;
46124611
final int offsetValue = offset + fetchCount.intValue();
4613-
jdbcParameters.addParameter( offsetParameter );
4614-
parameterBinders.add(
4612+
final int parameterPosition = addParameterBinder(
4613+
offsetParameter,
46154614
(statement, startPosition, jdbcParameterBindings, executionContext) -> {
46164615
final JdbcParameterBinding binding = jdbcParameterBindings.getBinding( offsetParameter );
46174616
if ( binding == null ) {
@@ -4627,44 +4626,29 @@ protected void renderFetchPlusOffsetExpressionAsSingleParameter(
46274626
);
46284627
}
46294628
);
4629+
renderParameterAsParameter( parameterPosition, offsetParameter );
46304630
}
46314631
}
46324632
else {
4633-
appendSql( PARAM_MARKER );
46344633
final JdbcParameter offsetParameter = (JdbcParameter) offsetClauseExpression;
46354634
final JdbcParameter fetchParameter = (JdbcParameter) fetchClauseExpression;
4636-
final OffsetReceivingParameterBinder fetchBinder = new OffsetReceivingParameterBinder(
4635+
final FetchPlusOffsetParameterBinder fetchBinder = new FetchPlusOffsetParameterBinder(
46374636
offsetParameter,
46384637
fetchParameter,
46394638
offset
46404639
);
4641-
// We don't register and bind the special OffsetJdbcParameter as that comes from the query options
4642-
// And in this case, we only want to bind a single JDBC parameter
4643-
if ( !( offsetParameter instanceof OffsetJdbcParameter ) ) {
4644-
jdbcParameters.addParameter( offsetParameter );
4645-
parameterBinders.add(
4646-
(statement, startPosition, jdbcParameterBindings, executionContext) -> {
4647-
final JdbcParameterBinding binding = jdbcParameterBindings.getBinding( offsetParameter );
4648-
if ( binding == null ) {
4649-
throw new ExecutionException( "JDBC parameter value not bound - " + offsetParameter );
4650-
}
4651-
fetchBinder.dynamicOffset = (Number) binding.getBindValue();
4652-
}
4653-
);
4654-
}
4655-
jdbcParameters.addParameter( fetchParameter );
4656-
parameterBinders.add( fetchBinder );
4640+
final int parameterPosition = addParameterBinder( fetchParameter, fetchBinder );
4641+
renderParameterAsParameter( parameterPosition, fetchParameter );
46574642
}
46584643
}
46594644

4660-
private static class OffsetReceivingParameterBinder implements JdbcParameterBinder {
4645+
private static class FetchPlusOffsetParameterBinder implements JdbcParameterBinder {
46614646

46624647
private final JdbcParameter offsetParameter;
46634648
private final JdbcParameter fetchParameter;
46644649
private final int staticOffset;
4665-
private Number dynamicOffset;
46664650

4667-
public OffsetReceivingParameterBinder(
4651+
public FetchPlusOffsetParameterBinder(
46684652
JdbcParameter offsetParameter,
46694653
JdbcParameter fetchParameter,
46704654
int staticOffset) {
@@ -4695,8 +4679,11 @@ public void bindParameterValue(
46954679
offsetValue = executionContext.getQueryOptions().getEffectiveLimit().getFirstRow();
46964680
}
46974681
else {
4698-
offsetValue = dynamicOffset.intValue() + staticOffset;
4699-
dynamicOffset = null;
4682+
final JdbcParameterBinding binding = jdbcParameterBindings.getBinding( offsetParameter );
4683+
if ( binding == null ) {
4684+
throw new ExecutionException( "JDBC parameter value not bound - " + offsetParameter );
4685+
}
4686+
offsetValue = ((Number) binding.getBindValue()).intValue() + staticOffset;
47004687
}
47014688
//noinspection unchecked
47024689
fetchParameter.getExpressionType().getSingleJdbcMapping().getJdbcValueBinder().bind(
@@ -5670,15 +5657,15 @@ protected void renderLiteral(Literal literal, boolean castParameter) {
56705657
final JdbcLiteralFormatter<Object> literalFormatter = literal.getJdbcMapping().getJdbcLiteralFormatter();
56715658
// If we encounter a plain literal in the select clause which has no literal formatter, we must render it as parameter
56725659
if ( literalFormatter == null ) {
5673-
parameterBinders.add( literal );
5660+
final int parameterPosition = addParameterBinderOnly( literal );
56745661
final JdbcType jdbcType = literal.getJdbcMapping().getJdbcType();
5675-
final String marker = parameterMarkerStrategy.createMarker( parameterBinders.size(), jdbcType );
5676-
final LiteralAsParameter<?> jdbcParameter = new LiteralAsParameter<>( literal, marker );
5662+
final String marker = parameterMarkerStrategy.createMarker( parameterPosition, jdbcType );
5663+
56775664
if ( castParameter ) {
5678-
renderCasted( jdbcParameter );
5665+
renderCasted( new LiteralAsParameter<>( literal, marker ) );
56795666
}
56805667
else {
5681-
jdbcParameter.renderToSql( this, this, sessionFactory );
5668+
jdbcType.appendWriteExpression( marker, this, dialect );
56825669
}
56835670
}
56845671
else {
@@ -7002,15 +6989,8 @@ public void visitParameter(JdbcParameter jdbcParameter) {
70026989
}
70036990

70046991
protected void visitParameterAsParameter(JdbcParameter jdbcParameter) {
7005-
renderParameterAsParameter( jdbcParameter );
7006-
parameterBinders.add( jdbcParameter.getParameterBinder() );
7007-
jdbcParameters.addParameter( jdbcParameter );
7008-
}
7009-
7010-
protected final void renderParameterAsParameter(JdbcParameter jdbcParameter) {
7011-
final JdbcType jdbcType = jdbcParameter.getExpressionType().getJdbcMapping( 0 ).getJdbcType();
7012-
assert jdbcType != null;
7013-
renderParameterAsParameter( parameterBinders.size() + 1, jdbcParameter );
6992+
final int parameterPosition = addParameterBinder( jdbcParameter );
6993+
renderParameterAsParameter( parameterPosition, jdbcParameter );
70146994
}
70156995

70166996
protected void renderWrappedParameter(JdbcParameter jdbcParameter) {
@@ -7037,6 +7017,53 @@ protected void renderParameterAsParameter(int position, JdbcParameter jdbcParame
70377017
jdbcType.appendWriteExpression( parameterMarker, this, dialect );
70387018
}
70397019

7020+
protected final int addParameterBinder(JdbcParameter parameter) {
7021+
return addParameterBinder( parameter, parameter.getParameterBinder() );
7022+
}
7023+
7024+
protected final int addParameterBinder(JdbcParameter parameter, JdbcParameterBinder parameterBinder) {
7025+
final Integer parameterId = parameter.getParameterId();
7026+
if ( ParameterMarkerStrategyStandard.isStandardRenderer( parameterMarkerStrategy )
7027+
// Filter parameters are unique and they are not tracked via parameterInfo
7028+
|| parameter instanceof FilterJdbcParameter
7029+
|| parameterId == null ) {
7030+
return addParameterBinderOnly( parameterBinder );
7031+
}
7032+
else {
7033+
parameterIdToBinderIndex = ensureCapacity( parameterIdToBinderIndex, parameterId + 1 );
7034+
int binderIndex = parameterIdToBinderIndex[parameterId];
7035+
if ( binderIndex == -1 ) {
7036+
parameterIdToBinderIndex[parameterId] = binderIndex = addParameterBinderOnly( parameterBinder );
7037+
}
7038+
return binderIndex;
7039+
}
7040+
}
7041+
7042+
private static int[] ensureCapacity(int[] array, int minCapacity) {
7043+
int oldCapacity;
7044+
if ( array == null ) {
7045+
oldCapacity = 0;
7046+
array = new int[minCapacity];
7047+
}
7048+
else {
7049+
oldCapacity = array.length;
7050+
if ( minCapacity > oldCapacity ) {
7051+
int newCapacity = oldCapacity + (oldCapacity >> 1);
7052+
newCapacity = Math.max( Math.max( newCapacity, minCapacity ), 10 );
7053+
array = Arrays.copyOf( array, newCapacity );
7054+
}
7055+
}
7056+
for ( int i = oldCapacity; i < array.length; i++ ) {
7057+
array[i] = -1;
7058+
}
7059+
return array;
7060+
}
7061+
7062+
private int addParameterBinderOnly(JdbcParameterBinder parameterBinder) {
7063+
parameterBinders.add( parameterBinder );
7064+
return parameterBinders.size();
7065+
}
7066+
70407067
@Override
70417068
public void render(SqlAstNode sqlAstNode, SqlAstNodeRenderingMode renderingMode) {
70427069
final SqlAstNodeRenderingMode original = this.parameterRenderingMode;
@@ -8495,7 +8522,7 @@ public void visitCustomTableInsert(TableInsertCustomSql tableInsert) {
84958522
assert sqlBuffer.toString().isEmpty();
84968523
sqlBuffer.append( tableInsert.getCustomSql() );
84978524

8498-
tableInsert.forEachParameter( this::applyParameter );
8525+
tableInsert.forEachParameter( this::addParameterBinder );
84998526
}
85008527

85018528
@Override
@@ -8604,7 +8631,7 @@ public void visitCustomTableUpdate(TableUpdateCustomSql tableUpdate) {
86048631
assert sqlBuffer.toString().isEmpty();
86058632
sqlBuffer.append( tableUpdate.getCustomSql() );
86068633

8607-
tableUpdate.forEachParameter( this::applyParameter );
8634+
tableUpdate.forEachParameter( this::addParameterBinder );
86088635
}
86098636

86108637
@Override
@@ -8668,13 +8695,7 @@ public void visitCustomTableDelete(TableDeleteCustomSql tableDelete) {
86688695
assert sqlBuffer.toString().isEmpty();
86698696
sqlBuffer.append( tableDelete.getCustomSql() );
86708697

8671-
tableDelete.forEachParameter( this::applyParameter );
8672-
}
8673-
8674-
protected void applyParameter(ColumnValueParameter parameter) {
8675-
assert parameter != null;
8676-
parameterBinders.add( parameter.getParameterBinder() );
8677-
jdbcParameters.addParameter( parameter );
8698+
tableDelete.forEachParameter( this::addParameterBinder );
86788699
}
86798700

86808701
@Override
@@ -8711,10 +8732,6 @@ public void visitColumnWriteFragment(ColumnWriteFragment columnWriteFragment) {
87118732

87128733
protected void simpleColumnWriteFragmentRendering(ColumnWriteFragment columnWriteFragment) {
87138734
appendSql( columnWriteFragment.getFragment() );
8714-
8715-
for ( ColumnValueParameter parameter : columnWriteFragment.getParameters() ) {
8716-
parameterBinders.add( parameter.getParameterBinder() );
8717-
jdbcParameters.addParameter( parameter );
8718-
}
8735+
columnWriteFragment.getParameters().forEach( this::addParameterBinder );
87198736
}
87208737
}

hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/JdbcParameter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
*/
55
package org.hibernate.sql.ast.tree.expression;
66

7+
import org.checkerframework.checker.nullness.qual.Nullable;
78
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
89

910
/**
1011
* @author Steve Ebersole
1112
*/
1213
public interface JdbcParameter extends Expression {
1314
JdbcParameterBinder getParameterBinder();
15+
@Nullable Integer getParameterId();
1416
}

hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.sql.PreparedStatement;
88
import java.sql.SQLException;
99

10+
import org.checkerframework.checker.nullness.qual.Nullable;
1011
import org.hibernate.cache.MutableCacheKeyBuilder;
1112
import org.hibernate.engine.internal.CacheHelper;
1213
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@@ -36,9 +37,15 @@ public abstract class AbstractJdbcParameter
3637
implements JdbcParameter, JdbcParameterBinder, MappingModelExpressible, SqlExpressible, BasicValuedMapping {
3738

3839
private final JdbcMapping jdbcMapping;
40+
private final @Nullable Integer parameterId;
3941

4042
public AbstractJdbcParameter(JdbcMapping jdbcMapping) {
43+
this( jdbcMapping, null );
44+
}
45+
46+
public AbstractJdbcParameter(JdbcMapping jdbcMapping, @Nullable Integer parameterId) {
4147
this.jdbcMapping = jdbcMapping;
48+
this.parameterId = parameterId;
4249
}
4350

4451
@Override
@@ -56,6 +63,11 @@ public MappingType getMappedType() {
5663
return jdbcMapping;
5764
}
5865

66+
@Override
67+
public @Nullable Integer getParameterId() {
68+
return parameterId;
69+
}
70+
5971
@Override
6072
public void accept(SqlAstWalker sqlTreeWalker) {
6173
sqlTreeWalker.visitParameter( this );

hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcParameterImpl.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ public class JdbcParameterImpl extends AbstractJdbcParameter {
1414
public JdbcParameterImpl(JdbcMapping jdbcMapping) {
1515
super( jdbcMapping );
1616
}
17+
18+
public JdbcParameterImpl(JdbcMapping jdbcMapping, Integer parameterId) {
19+
super( jdbcMapping, parameterId );
20+
}
1721
}

hibernate-core/src/main/java/org/hibernate/sql/exec/internal/SqlTypedMappingJdbcParameter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package org.hibernate.sql.exec.internal;
66

7+
import org.checkerframework.checker.nullness.qual.Nullable;
78
import org.hibernate.metamodel.mapping.SqlTypedMapping;
89

910
/**
@@ -18,6 +19,11 @@ public SqlTypedMappingJdbcParameter(SqlTypedMapping sqlTypedMapping) {
1819
this.sqlTypedMapping = sqlTypedMapping;
1920
}
2021

22+
public SqlTypedMappingJdbcParameter(SqlTypedMapping sqlTypedMapping, @Nullable Integer parameterId) {
23+
super( sqlTypedMapping.getJdbcMapping(), parameterId );
24+
this.sqlTypedMapping = sqlTypedMapping;
25+
}
26+
2127
public SqlTypedMapping getSqlTypedMapping() {
2228
return sqlTypedMapping;
2329
}

0 commit comments

Comments
 (0)