Skip to content

Commit 04abbe9

Browse files
author
nathan.xu
committed
HHH-16283 - Integrate ParameterMarkerStrategy into NativeQuery
1 parent afca931 commit 04abbe9

File tree

7 files changed

+110
-74
lines changed

7 files changed

+110
-74
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/pagination/LimitHandler.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import org.hibernate.query.spi.Limit;
1313
import org.hibernate.query.spi.QueryOptions;
14+
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
1415

1516
/**
1617
* Contract defining dialect-specific limit and offset handling.
@@ -47,6 +48,10 @@ default String processSql(String sql, Limit limit, QueryOptions queryOptions) {
4748
return processSql( sql, limit );
4849
}
4950

51+
default String processSql(String sql, Limit limit, QueryOptions queryOptions, ParameterMarkerStrategy parameterMarkerStrategy, int limitParameterStartPosition) {
52+
return processSql( sql, limit, queryOptions );
53+
}
54+
5055
int bindLimitParametersAtStartOfQuery(Limit limit, PreparedStatement statement, int index) throws SQLException;
5156

5257
int bindLimitParametersAtEndOfQuery(Limit limit, PreparedStatement statement, int index) throws SQLException;

hibernate-core/src/main/java/org/hibernate/dialect/pagination/OffsetFetchLimitHandler.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
package org.hibernate.dialect.pagination;
88

99
import org.hibernate.query.spi.Limit;
10+
import org.hibernate.query.spi.QueryOptions;
11+
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
12+
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
13+
import org.hibernate.type.descriptor.jdbc.IntegerJdbcType;
1014

1115
/**
1216
* A {@link LimitHandler} for databases which support the
@@ -27,6 +31,11 @@ public OffsetFetchLimitHandler(boolean variableLimit) {
2731

2832
@Override
2933
public String processSql(String sql, Limit limit) {
34+
return processSql( sql, limit, null, ParameterMarkerStrategyStandard.INSTANCE, 0 );
35+
}
36+
37+
@Override
38+
public String processSql(String sql, Limit limit, QueryOptions queryOptions, ParameterMarkerStrategy parameterMarkerStrategy, int limitParameterStartPosition) {
3039

3140
boolean hasFirstRow = hasFirstRow(limit);
3241
boolean hasMaxRows = hasMaxRows(limit);
@@ -42,7 +51,7 @@ public String processSql(String sql, Limit limit) {
4251
if ( hasFirstRow ) {
4352
offsetFetch.append( " offset " );
4453
if ( supportsVariableLimit() ) {
45-
offsetFetch.append( "?" );
54+
offsetFetch.append( parameterMarkerStrategy.createMarker( limitParameterStartPosition++, IntegerJdbcType.INSTANCE ) );
4655
}
4756
else {
4857
offsetFetch.append( limit.getFirstRow() );
@@ -60,7 +69,7 @@ public String processSql(String sql, Limit limit) {
6069
offsetFetch.append( " fetch first " );
6170
}
6271
if ( supportsVariableLimit() ) {
63-
offsetFetch.append( "?" );
72+
offsetFetch.append( parameterMarkerStrategy.createMarker( limitParameterStartPosition++, IntegerJdbcType.INSTANCE ) );
6473
}
6574
else {
6675
offsetFetch.append( getMaxOrLimit( limit ) );

hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
import org.hibernate.query.internal.DelegatingDomainQueryExecutionContext;
5656
import org.hibernate.query.internal.ParameterMetadataImpl;
5757
import org.hibernate.query.internal.QueryOptionsImpl;
58-
import org.hibernate.query.internal.QueryParameterBindingsImpl;
5958
import org.hibernate.query.internal.ResultSetMappingResolutionContext;
6059
import org.hibernate.query.named.NamedResultSetMappingMemento;
6160
import org.hibernate.query.results.Builders;
@@ -90,6 +89,7 @@
9089
import org.hibernate.query.sql.spi.ParameterInterpretation;
9190
import org.hibernate.query.sql.spi.ParameterOccurrence;
9291
import org.hibernate.query.sql.spi.SelectInterpretationsKey;
92+
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
9393
import org.hibernate.sql.exec.internal.CallbackImpl;
9494
import org.hibernate.sql.exec.spi.Callback;
9595
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
@@ -117,6 +117,7 @@
117117

118118
/**
119119
* @author Steve Ebersole
120+
* @author Nathan Xu
120121
*/
121122
public class NativeQueryImpl<R>
122123
extends AbstractQuery<R>
@@ -370,7 +371,8 @@ private ParameterInterpretation resolveParameterInterpretation(
370371
return interpretationCache.resolveNativeQueryParameters(
371372
sqlString,
372373
s -> {
373-
final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl();
374+
final ParameterMarkerStrategy parameterMarkerStrategy = sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
375+
final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl( parameterMarkerStrategy );
374376

375377
session.getFactory().getServiceRegistry()
376378
.requireService( NativeQueryInterpreter.class )
@@ -736,23 +738,36 @@ protected String expandParameterLists() {
736738
// Some DBs limit number of IN expressions. For now, warn...
737739
final SessionFactoryImplementor sessionFactory = getSessionFactory();
738740
final Dialect dialect = sessionFactory.getJdbcServices().getDialect();
741+
742+
final ParameterMarkerStrategy parameterMarkerStrategy = sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
743+
739744
final boolean paddingEnabled = sessionFactory.getSessionFactoryOptions().inClauseParameterPaddingEnabled();
740745
final int inExprLimit = dialect.getInExpressionCountLimit();
741746

742747
StringBuilder sb = null;
748+
StringBuilder occurrenceExpansionSB = null;
743749

744750
// Handle parameter lists
745-
int offset = 0;
746-
for ( ParameterOccurrence occurrence : parameterOccurrences ) {
751+
int sourceOffset = 0;
752+
int expandedParamPosition = 1;
753+
for ( int originalParamPosition = 1; originalParamPosition <= parameterOccurrences.size(); originalParamPosition++ ) {
754+
final ParameterOccurrence occurrence = parameterOccurrences.get( originalParamPosition - 1 );
747755
final QueryParameterImplementor<?> queryParameter = occurrence.getParameter();
748756
final QueryParameterBinding<?> binding = parameterBindings.getBinding( queryParameter );
749757
if ( !binding.isMultiValued() ) {
758+
if ( originalParamPosition != expandedParamPosition ) {
759+
if ( sb == null ) {
760+
sb = new StringBuilder( sqlString );
761+
}
762+
sourceOffset = getNewSourceOffsetAfterReplacement( sb, sourceOffset, occurrence, parameterMarkerStrategy.createMarker( expandedParamPosition, null ) );
763+
}
764+
expandedParamPosition++;
750765
continue;
751766
}
752767
final Collection<?> bindValues = binding.getBindValues();
753768

754-
int bindValueCount = bindValues.size();
755-
int bindValueMaxCount = determineBindValueMaxCount( paddingEnabled, inExprLimit, bindValueCount );
769+
final int bindValueCount = bindValues.size();
770+
final int bindValueMaxCount = determineBindValueMaxCount( paddingEnabled, inExprLimit, bindValueCount );
756771

757772
if ( inExprLimit > 0 && bindValueCount > inExprLimit ) {
758773
log.tooManyInExpressions(
@@ -767,6 +782,7 @@ protected String expandParameterLists() {
767782

768783
final int sourcePosition = occurrence.getSourcePosition();
769784
if ( sourcePosition < 0 ) {
785+
expandedParamPosition++;
770786
continue;
771787
}
772788

@@ -781,7 +797,7 @@ protected String expandParameterLists() {
781797
}
782798
}
783799
if ( isEnclosedInParens ) {
784-
for ( int i = sourcePosition + 1; i < sqlString.length(); i++ ) {
800+
for ( int i = sourcePosition + occurrence.getLength(); i < sqlString.length(); i++ ) {
785801
final char ch = sqlString.charAt( i );
786802
if ( !Character.isWhitespace( ch ) ) {
787803
isEnclosedInParens = ch == ')';
@@ -790,62 +806,60 @@ protected String expandParameterLists() {
790806
}
791807
}
792808

793-
if ( bindValueCount == 1 && isEnclosedInParens ) {
809+
if ( bindValueCount == 1 && isEnclosedInParens && expandedParamPosition == originalParamPosition ) {
794810
// short-circuit for performance when only 1 value and the
795811
// placeholder is already enclosed in parentheses...
812+
expandedParamPosition++;
796813
continue;
797814
}
798815

799816
if ( sb == null ) {
800-
sb = new StringBuilder( sqlString.length() + 20 );
801-
sb.append( sqlString );
817+
sb = new StringBuilder( sqlString );
818+
}
819+
820+
if ( occurrenceExpansionSB == null ) {
821+
occurrenceExpansionSB = new StringBuilder();
822+
} else {
823+
occurrenceExpansionSB.setLength( 0 );
824+
}
825+
826+
if ( !isEnclosedInParens ) {
827+
occurrenceExpansionSB.append( '(' );
802828
}
803829

804-
final String expansionListAsString;
805830
// HHH-8901
806831
if ( bindValueMaxCount == 0 ) {
807-
if ( isEnclosedInParens ) {
808-
expansionListAsString = "null";
809-
}
810-
else {
811-
expansionListAsString = "(null)";
812-
}
813-
}
814-
else {
815-
// Shift 1 bit instead of multiplication by 2
816-
char[] chars;
817-
if ( isEnclosedInParens ) {
818-
chars = new char[( bindValueMaxCount << 1 ) - 1];
819-
chars[0] = '?';
820-
for ( int i = 1; i < bindValueMaxCount; i++ ) {
821-
final int index = i << 1;
822-
chars[index - 1] = ',';
823-
chars[index] = '?';
832+
occurrenceExpansionSB.append( "null" );
833+
} else {
834+
for ( int i = 0; i < bindValueMaxCount; i++ ) {
835+
final String marker = parameterMarkerStrategy.createMarker(
836+
expandedParamPosition + i,
837+
null
838+
);
839+
occurrenceExpansionSB.append( marker );
840+
if ( i + 1 < bindValueMaxCount ) {
841+
occurrenceExpansionSB.append( ',' );
824842
}
825843
}
826-
else {
827-
chars = new char[( bindValueMaxCount << 1 ) + 1];
828-
chars[0] = '(';
829-
chars[1] = '?';
830-
for ( int i = 1; i < bindValueMaxCount; i++ ) {
831-
final int index = i << 1;
832-
chars[index] = ',';
833-
chars[index + 1] = '?';
834-
}
835-
chars[chars.length - 1] = ')';
836-
}
837-
838-
expansionListAsString = new String(chars);
839844
}
845+
if ( !isEnclosedInParens ) {
846+
occurrenceExpansionSB.append( ')' );
847+
}
848+
849+
sourceOffset = getNewSourceOffsetAfterReplacement( sb, sourceOffset, occurrence, occurrenceExpansionSB.toString() );
840850

841-
final int start = sourcePosition + offset;
842-
final int end = start + 1;
843-
sb.replace( start, end, expansionListAsString );
844-
offset += expansionListAsString.length() - 1;
851+
expandedParamPosition += bindValueMaxCount;
845852
}
846853
return sb == null ? sqlString : sb.toString();
847854
}
848855

856+
private int getNewSourceOffsetAfterReplacement(StringBuilder sb, int sourceOffset, ParameterOccurrence occurrence, String replacement) {
857+
final int start = occurrence.getSourcePosition() + sourceOffset;
858+
final int end = start + occurrence.getLength();
859+
sb.replace( start, end, replacement );
860+
return sourceOffset + ( replacement.length() - occurrence.getLength() );
861+
}
862+
849863
public static int determineBindValueMaxCount(boolean paddingEnabled, int inExprLimit, int bindValueCount) {
850864
int bindValueMaxCount = bindValueCount;
851865

hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import org.hibernate.query.spi.QueryParameterImplementor;
2121
import org.hibernate.query.sql.spi.ParameterOccurrence;
2222
import org.hibernate.query.sql.spi.ParameterRecognizer;
23+
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
24+
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
2325

2426
/**
2527
* @author Steve Ebersole
@@ -36,13 +38,15 @@ private enum ParameterStyle {
3638
private Map<String, QueryParameterImplementor<?>> namedQueryParameters;
3739
private Map<Integer, QueryParameterImplementor<?>> positionalQueryParameters;
3840

39-
private int ordinalParameterImplicitPosition;
41+
private int parameterImplicitPosition;
42+
private final ParameterMarkerStrategy parameterMarkerStrategy;
4043

4144
private List<ParameterOccurrence> parameterList;
4245
private final StringBuilder sqlStringBuffer = new StringBuilder();
4346

44-
public ParameterRecognizerImpl() {
45-
ordinalParameterImplicitPosition = 1;
47+
public ParameterRecognizerImpl(ParameterMarkerStrategy parameterMarkerStrategy) {
48+
this.parameterMarkerStrategy = parameterMarkerStrategy == null ? ParameterMarkerStrategyStandard.INSTANCE : parameterMarkerStrategy;
49+
parameterImplicitPosition = 1;
4650
}
4751

4852
@Override
@@ -103,7 +107,7 @@ else if ( parameterStyle != ParameterStyle.JDBC ) {
103107
throw new ParameterRecognitionException( "Cannot mix parameter styles between JDBC-style, ordinal and named in the same query" );
104108
}
105109

106-
int implicitPosition = ordinalParameterImplicitPosition++;
110+
int implicitPosition = parameterImplicitPosition++;
107111

108112
QueryParameterImplementor<?> parameter = null;
109113

@@ -119,12 +123,7 @@ else if ( parameterStyle != ParameterStyle.JDBC ) {
119123
positionalQueryParameters.put( implicitPosition, parameter );
120124
}
121125

122-
if ( parameterList == null ) {
123-
parameterList = new ArrayList<>();
124-
}
125-
126-
parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );
127-
sqlStringBuffer.append( "?" );
126+
recognizeParameter( parameter, implicitPosition );
128127
}
129128

130129
@Override
@@ -150,12 +149,7 @@ else if ( parameterStyle != ParameterStyle.NAMED ) {
150149
namedQueryParameters.put( name, parameter );
151150
}
152151

153-
if ( parameterList == null ) {
154-
parameterList = new ArrayList<>();
155-
}
156-
157-
parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );
158-
sqlStringBuffer.append( "?" );
152+
recognizeParameter( parameter, parameterImplicitPosition++ );
159153
}
160154

161155
@Override
@@ -185,16 +179,21 @@ else if ( parameterStyle != ParameterStyle.NAMED ) {
185179
positionalQueryParameters.put( position, parameter );
186180
}
187181

188-
if ( parameterList == null ) {
189-
parameterList = new ArrayList<>();
190-
}
191-
192-
parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );
193-
sqlStringBuffer.append( "?" );
182+
recognizeParameter( parameter, parameterImplicitPosition++ );
194183
}
195184

196185
@Override
197186
public void other(char character) {
198187
sqlStringBuffer.append( character );
199188
}
189+
190+
private void recognizeParameter(QueryParameterImplementor parameter, int position) {
191+
final String marker = parameterMarkerStrategy.createMarker( position, null );
192+
final int markerLength = marker.length();
193+
if ( parameterList == null ) {
194+
parameterList = new ArrayList<>();
195+
}
196+
sqlStringBuffer.append( marker );
197+
parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() - markerLength, markerLength ) );
198+
}
200199
}

hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterOccurrence.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ public final class ParameterOccurrence {
1515

1616
private final QueryParameterImplementor<?> parameter;
1717
private final int sourcePosition;
18+
private final int length;
1819

19-
public ParameterOccurrence(QueryParameterImplementor<?> parameter, int sourcePosition) {
20+
public ParameterOccurrence(QueryParameterImplementor<?> parameter, int sourcePosition, int length) {
2021
this.parameter = parameter;
2122
this.sourcePosition = sourcePosition;
23+
this.length = length;
2224
}
2325

2426
public QueryParameterImplementor<?> getParameter() {
@@ -28,4 +30,8 @@ public QueryParameterImplementor<?> getParameter() {
2830
public int getSourcePosition() {
2931
return sourcePosition;
3032
}
33+
34+
public int getLength() {
35+
return length;
36+
}
3137
}

hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.hibernate.query.spi.QueryOptions;
3030
import org.hibernate.resource.jdbc.spi.JdbcSessionContext;
3131
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
32+
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
3233
import org.hibernate.sql.exec.spi.ExecutionContext;
3334
import org.hibernate.sql.exec.spi.JdbcLockStrategy;
3435
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
@@ -94,7 +95,10 @@ public DeferredResultSetAccess(
9495
sql = limitHandler.processSql(
9596
jdbcSelect.getSqlString(),
9697
limit,
97-
queryOptions
98+
queryOptions,
99+
executionContext.getSession().getFactory().getServiceRegistry().getService(
100+
ParameterMarkerStrategy.class ),
101+
jdbcParameterBindings.getBindings().size() + 1
98102
);
99103
}
100104

0 commit comments

Comments
 (0)