Skip to content

Commit 7fc85c6

Browse files
author
nathan.xu
committed
HHH-16283 - Integrate ParameterMarkerStrategy into NativeQuery
1 parent 2c607e4 commit 7fc85c6

File tree

15 files changed

+349
-109
lines changed

15 files changed

+349
-109
lines changed

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
*/
55
package org.hibernate.dialect.pagination;
66

7+
import java.util.function.Supplier;
8+
79
import org.hibernate.query.spi.Limit;
10+
import org.hibernate.query.spi.QueryOptions;
811

912
/**
1013
* A {@link LimitHandler} for databases which support the
@@ -24,7 +27,7 @@ public OffsetFetchLimitHandler(boolean variableLimit) {
2427
}
2528

2629
@Override
27-
public String processSql(String sql, Limit limit) {
30+
public String processSql(String sql, Limit limit, QueryOptions queryOptions) {
2831

2932
boolean hasFirstRow = hasFirstRow(limit);
3033
boolean hasMaxRows = hasMaxRows(limit);
@@ -37,10 +40,14 @@ public String processSql(String sql, Limit limit) {
3740

3841
begin(sql, offsetFetch, hasFirstRow, hasMaxRows);
3942

43+
Supplier<String> parameterMarkerSupplier = ( queryOptions == null || queryOptions.getLimitHandlerParameterMarkerSupplier() == null )
44+
? () -> "?"
45+
: queryOptions.getLimitHandlerParameterMarkerSupplier();
46+
4047
if ( hasFirstRow ) {
4148
offsetFetch.append( " offset " );
4249
if ( supportsVariableLimit() ) {
43-
offsetFetch.append( "?" );
50+
offsetFetch.append( parameterMarkerSupplier.get() );
4451
}
4552
else {
4653
offsetFetch.append( limit.getFirstRow() );
@@ -58,7 +65,7 @@ public String processSql(String sql, Limit limit) {
5865
offsetFetch.append( " fetch first " );
5966
}
6067
if ( supportsVariableLimit() ) {
61-
offsetFetch.append( "?" );
68+
offsetFetch.append( parameterMarkerSupplier.get() );
6269
}
6370
else {
6471
offsetFetch.append( getMaxOrLimit( limit ) );

hibernate-core/src/main/java/org/hibernate/query/internal/QueryOptionsImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.HashSet;
1010
import java.util.List;
1111
import java.util.Set;
12+
import java.util.function.Supplier;
1213

1314
import jakarta.persistence.CacheRetrieveMode;
1415
import jakarta.persistence.CacheStoreMode;
@@ -52,6 +53,17 @@ public class QueryOptionsImpl implements MutableQueryOptions, AppliedGraph {
5253
private Set<String> enabledFetchProfiles;
5354
private Set<String> disabledFetchProfiles;
5455

56+
@Nullable
57+
private final Supplier<String> limitHandlerParameterMarkerSupplier;
58+
59+
public QueryOptionsImpl() {
60+
this( null );
61+
}
62+
63+
public QueryOptionsImpl(Supplier<String> limitHandlerParameterMarkerSupplier) {
64+
this.limitHandlerParameterMarkerSupplier = limitHandlerParameterMarkerSupplier;
65+
}
66+
5567
@Override
5668
public Integer getTimeout() {
5769
return timeout;
@@ -255,4 +267,9 @@ public AppliedGraph getAppliedGraph() {
255267
public @Nullable GraphSemantic getSemantic() {
256268
return graphSemantic;
257269
}
270+
271+
@Override
272+
public @Nullable Supplier<String> getLimitHandlerParameterMarkerSupplier() {
273+
return this.limitHandlerParameterMarkerSupplier;
274+
}
258275
}

hibernate-core/src/main/java/org/hibernate/query/spi/QueryOptions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.sql.Statement;
88
import java.util.List;
99
import java.util.Set;
10+
import java.util.function.Supplier;
1011

1112
import jakarta.persistence.CacheRetrieveMode;
1213
import jakarta.persistence.CacheStoreMode;
@@ -193,6 +194,10 @@ default ListResultsConsumer.UniqueSemantic getUniqueSemantic(){
193194
return null;
194195
}
195196

197+
default Supplier<String> getLimitHandlerParameterMarkerSupplier() {
198+
return () -> "?";
199+
}
200+
196201
/**
197202
* Provide singleton access for frequently needed options:
198203
*/

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
import org.hibernate.query.spi.DomainQueryExecutionContext;
1515
import org.hibernate.query.spi.NonSelectQueryPlan;
1616
import org.hibernate.query.spi.QueryParameterBindings;
17+
import org.hibernate.query.sql.spi.NativeQueryPlan;
1718
import org.hibernate.query.sql.spi.ParameterOccurrence;
18-
import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
1919
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
2020
import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation;
2121
import org.hibernate.sql.exec.spi.JdbcOperationQueryMutationNative;
@@ -25,7 +25,7 @@
2525
/**
2626
* @author Steve Ebersole
2727
*/
28-
public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan {
28+
public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan, NativeQueryPlan {
2929
private final String sql;
3030
private final Set<String> affectedTableNames;
3131

@@ -79,7 +79,7 @@ public int executeUpdate(DomainQueryExecutionContext executionContext) {
7979
.getStatementPreparer()
8080
.prepareStatement( sql ),
8181
(integer, preparedStatement) -> {},
82-
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext )
82+
getSqmJdbcExecutionContext( executionContext, jdbcParameterBindings )
8383
);
8484
}
8585
}

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

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import org.hibernate.query.sql.spi.ParameterInterpretation;
8989
import org.hibernate.query.sql.spi.ParameterOccurrence;
9090
import org.hibernate.query.sql.spi.SelectInterpretationsKey;
91+
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
9192
import org.hibernate.sql.exec.internal.CallbackImpl;
9293
import org.hibernate.sql.exec.spi.Callback;
9394
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
@@ -115,6 +116,7 @@
115116

116117
/**
117118
* @author Steve Ebersole
119+
* @author Nathan Xu
118120
*/
119121
public class NativeQueryImpl<R>
120122
extends AbstractQuery<R>
@@ -368,7 +370,8 @@ private ParameterInterpretation resolveParameterInterpretation(
368370
return interpretationCache.resolveNativeQueryParameters(
369371
sqlString,
370372
s -> {
371-
final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl();
373+
final ParameterMarkerStrategy parameterMarkerStrategy = sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
374+
final ParameterRecognizerImpl parameterRecognizer = new ParameterRecognizerImpl( parameterMarkerStrategy );
372375

373376
session.getFactory().getServiceRegistry()
374377
.requireService( NativeQueryInterpreter.class )
@@ -734,23 +737,36 @@ protected String expandParameterLists() {
734737
// Some DBs limit number of IN expressions. For now, warn...
735738
final SessionFactoryImplementor sessionFactory = getSessionFactory();
736739
final Dialect dialect = sessionFactory.getJdbcServices().getDialect();
740+
741+
final ParameterMarkerStrategy parameterMarkerStrategy = sessionFactory.getServiceRegistry().getService( ParameterMarkerStrategy.class );
742+
737743
final boolean paddingEnabled = sessionFactory.getSessionFactoryOptions().inClauseParameterPaddingEnabled();
738744
final int inExprLimit = dialect.getInExpressionCountLimit();
739745

740746
StringBuilder sb = null;
747+
StringBuilder occurrenceExpansionSB = null;
741748

742749
// Handle parameter lists
743-
int offset = 0;
744-
for ( ParameterOccurrence occurrence : parameterOccurrences ) {
750+
int sourceOffset = 0;
751+
int expandedParamPosition = 1;
752+
for ( int originalParamPosition = 1; originalParamPosition <= parameterOccurrences.size(); originalParamPosition++ ) {
753+
final ParameterOccurrence occurrence = parameterOccurrences.get( originalParamPosition - 1 );
745754
final QueryParameterImplementor<?> queryParameter = occurrence.getParameter();
746755
final QueryParameterBinding<?> binding = parameterBindings.getBinding( queryParameter );
747756
if ( !binding.isMultiValued() ) {
757+
if ( originalParamPosition != expandedParamPosition ) {
758+
if ( sb == null ) {
759+
sb = new StringBuilder( sqlString );
760+
}
761+
sourceOffset = getNewSourceOffsetAfterReplacement( sb, sourceOffset, occurrence, parameterMarkerStrategy.createMarker( expandedParamPosition, null ) );
762+
}
763+
expandedParamPosition++;
748764
continue;
749765
}
750766
final Collection<?> bindValues = binding.getBindValues();
751767

752-
int bindValueCount = bindValues.size();
753-
int bindValueMaxCount = determineBindValueMaxCount( paddingEnabled, inExprLimit, bindValueCount );
768+
final int bindValueCount = bindValues.size();
769+
final int bindValueMaxCount = determineBindValueMaxCount( paddingEnabled, inExprLimit, bindValueCount );
754770

755771
if ( inExprLimit > 0 && bindValueCount > inExprLimit ) {
756772
log.tooManyInExpressions(
@@ -765,6 +781,7 @@ protected String expandParameterLists() {
765781

766782
final int sourcePosition = occurrence.getSourcePosition();
767783
if ( sourcePosition < 0 ) {
784+
expandedParamPosition++;
768785
continue;
769786
}
770787

@@ -779,7 +796,7 @@ protected String expandParameterLists() {
779796
}
780797
}
781798
if ( isEnclosedInParens ) {
782-
for ( int i = sourcePosition + 1; i < sqlString.length(); i++ ) {
799+
for ( int i = sourcePosition + occurrence.getLength(); i < sqlString.length(); i++ ) {
783800
final char ch = sqlString.charAt( i );
784801
if ( !Character.isWhitespace( ch ) ) {
785802
isEnclosedInParens = ch == ')';
@@ -788,62 +805,62 @@ protected String expandParameterLists() {
788805
}
789806
}
790807

791-
if ( bindValueCount == 1 && isEnclosedInParens ) {
808+
if ( bindValueCount == 1 && isEnclosedInParens && expandedParamPosition == originalParamPosition ) {
792809
// short-circuit for performance when only 1 value and the
793810
// placeholder is already enclosed in parentheses...
811+
expandedParamPosition++;
794812
continue;
795813
}
796814

797815
if ( sb == null ) {
798-
sb = new StringBuilder( sqlString.length() + 20 );
799-
sb.append( sqlString );
816+
sb = new StringBuilder( sqlString );
817+
}
818+
819+
if ( occurrenceExpansionSB == null ) {
820+
occurrenceExpansionSB = new StringBuilder();
821+
}
822+
else {
823+
occurrenceExpansionSB.setLength( 0 );
824+
}
825+
826+
if ( !isEnclosedInParens ) {
827+
occurrenceExpansionSB.append( '(' );
800828
}
801829

802-
final String expansionListAsString;
803830
// HHH-8901
804831
if ( bindValueMaxCount == 0 ) {
805-
if ( isEnclosedInParens ) {
806-
expansionListAsString = "null";
807-
}
808-
else {
809-
expansionListAsString = "(null)";
810-
}
832+
occurrenceExpansionSB.append( "null" );
811833
}
812834
else {
813-
// Shift 1 bit instead of multiplication by 2
814-
char[] chars;
815-
if ( isEnclosedInParens ) {
816-
chars = new char[( bindValueMaxCount << 1 ) - 1];
817-
chars[0] = '?';
818-
for ( int i = 1; i < bindValueMaxCount; i++ ) {
819-
final int index = i << 1;
820-
chars[index - 1] = ',';
821-
chars[index] = '?';
835+
for ( int i = 0; i < bindValueMaxCount; i++ ) {
836+
final String marker = parameterMarkerStrategy.createMarker(
837+
expandedParamPosition + i,
838+
null
839+
);
840+
occurrenceExpansionSB.append( marker );
841+
if ( i + 1 < bindValueMaxCount ) {
842+
occurrenceExpansionSB.append( ',' );
822843
}
823844
}
824-
else {
825-
chars = new char[( bindValueMaxCount << 1 ) + 1];
826-
chars[0] = '(';
827-
chars[1] = '?';
828-
for ( int i = 1; i < bindValueMaxCount; i++ ) {
829-
final int index = i << 1;
830-
chars[index] = ',';
831-
chars[index + 1] = '?';
832-
}
833-
chars[chars.length - 1] = ')';
834-
}
835-
836-
expansionListAsString = new String(chars);
837845
}
846+
if ( !isEnclosedInParens ) {
847+
occurrenceExpansionSB.append( ')' );
848+
}
849+
850+
sourceOffset = getNewSourceOffsetAfterReplacement( sb, sourceOffset, occurrence, occurrenceExpansionSB.toString() );
838851

839-
final int start = sourcePosition + offset;
840-
final int end = start + 1;
841-
sb.replace( start, end, expansionListAsString );
842-
offset += expansionListAsString.length() - 1;
852+
expandedParamPosition += bindValueMaxCount;
843853
}
844854
return sb == null ? sqlString : sb.toString();
845855
}
846856

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

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.hibernate.query.spi.ScrollableResultsImplementor;
2121
import org.hibernate.query.sql.spi.NativeSelectQueryPlan;
2222
import org.hibernate.query.sql.spi.ParameterOccurrence;
23-
import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
2423
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
2524
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
2625
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
@@ -90,7 +89,7 @@ public <T> T executeQuery(DomainQueryExecutionContext executionContext, ResultsC
9089
return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().executeQuery(
9190
jdbcSelect,
9291
jdbcParameterBindings,
93-
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ),
92+
getSqmJdbcExecutionContext( executionContext, jdbcParameterBindings ),
9493
null,
9594
null,
9695
-1,
@@ -133,7 +132,7 @@ public List<R> performList(DomainQueryExecutionContext executionContext) {
133132
return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().list(
134133
jdbcSelect,
135134
jdbcParameterBindings,
136-
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ),
135+
getSqmJdbcExecutionContext( executionContext, jdbcParameterBindings ),
137136
null,
138137
queryOptions.getUniqueSemantic() == null ?
139138
ListResultsConsumer.UniqueSemantic.NEVER :
@@ -177,7 +176,7 @@ public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, Doma
177176
jdbcSelect,
178177
scrollMode,
179178
jdbcParameterBindings,
180-
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ),
179+
getSqmJdbcExecutionContext( executionContext, jdbcParameterBindings ),
181180
null,
182181
-1
183182
);

0 commit comments

Comments
 (0)